πŸš€Day 22. 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.

πŸ” Example Walkthrough:

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:

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.

πŸ“ Solution Code

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