25. Boolean Parenthesization

The problem can be found at the following link: Question Link

Problem Description

Given a boolean expression s consisting of:

  • Operands:

    • 'T'True

    • 'F'False

  • Operators:

    • '&'Boolean AND

    • '|'Boolean OR

    • '^'Boolean XOR

Count the number of ways we can parenthesize the expression such that it evaluates to True.

Examples

Example 1:

Input:

Output:

Explanation:

The expression evaluates to True in 4 ways:

  1. ((T | T) & (F ^ T)) → (T | T) → T, (F ^ T) → T, T & T = T ✅

  2. (T | (T & (F ^ T))) → (F ^ T) → T, (T & T) → T, T | T = T ✅

  3. (((T | T) & F) ^ T) → (T | T) → T, (T & F) → F, F ^ T = T ✅

  4. (T | ((T & F) ^ T)) → (T & F) → F, (F ^ T) → T, T | T = T ✅

Example 2:

Input:

Output:

Explanation:

The expression evaluates to True in 2 ways:

  1. ((T^F)|F)

  2. (T^(F|F))

Constraints:

  • $(1 \leq |s| \leq 100)$

My Approach: Dynamic Programming (Bottom-Up Table)

The problem follows optimal substructure and overlapping subproblems, making Dynamic Programming (DP) the most efficient approach.

We use a bottom-up DP approach where we maintain two 2D DP tables:

  • T[i][j] → Number of ways to parenthesize s[i:j] to get True.

  • F[i][j] → Number of ways to parenthesize s[i:j] to get False.

Algorithm Steps:

  1. Initialize the DP tables:

    • If s[i] == 'T', set T[i][i] = 1 (since a single T is always True).

    • If s[i] == 'F', set F[i][i] = 1 (since a single F is always False).

  2. Fill the DP tables using a bottom-up approach:

    • Iterate over subexpressions of increasing length (l), from 2 to n.

    • For each subexpression s[i:j], check each operator s[k] (where k is an odd index).

    • Compute T[i][j] and F[i][j] using previously computed DP values.

  3. Update DP values based on the operator s[k]:

    • If s[k] == '&' (AND operator):

      • T[i][j] += lt * rt (both left and right must be True).

      • F[i][j] += lt * rf + lf * rt + lf * rf (any case where at least one is False).

    • If s[k] == '|' (OR operator):

      • T[i][j] += lt * rt + lt * rf + lf * rt (only both False make it False).

      • F[i][j] += lf * rf.

    • If s[k] == '^' (XOR operator):

      • T[i][j] += lt * rf + lf * rt (one True, one False).

      • F[i][j] += lt * rt + lf * rf (both True or both False).

  4. Return T[0][n-1], which gives the total ways to evaluate s to True.

Time and Auxiliary Space Complexity

  • Expected Time Complexity: O(N³), as we process all partitions (i, k, j) for each segment of s.

  • Expected Auxiliary Space Complexity: O(N²), for storing T and F DP tables.

Code (C++)

⚡ Alternative Approaches

1️⃣ Recursive + Memoization (O(N³) Time, O(N²) Space)

Algorithm Steps:

  1. Use Recursion to break the problem into smaller subproblems.

  2. Memoization avoids recomputing subproblems.

  3. Base Case:

    • If s[i] == 'T', return 1.

    • If s[i] == 'F', return 0.

  4. Recursive Case:

    • Divide expression at each operator (|, &, ^).

    • Compute left and right parts recursively.

    • Merge results based on the operator.

Time Complexity: O(N³) ✅ Space Complexity: O(N²)

Comparison of Approaches

Approach

⏱️ Time Complexity

🗂️ Space Complexity

Pros

⚠️ Cons

DP (Bottom-Up Table)

🟢 O(N³)

🟡 O(N²)

Faster, avoids recursion

Requires extra space

Recursive + Memoization

🟡 O(N³)

🔴 O(N²)

Simple, easy to understand

Uses extra memory

Best Choice?

  • If you want best efficiency: Use DP (Bottom-Up Table) approach.

  • If you like recursion with memoization: Use Recursive Approach.

Code (Java)

Code (Python)

Contribution and Support

For discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: Any Questions. Let’s make this learning journey more collaborative!

⭐ If you find this helpful, please give this repository a star! ⭐


📍Visitor Count

Last updated