Watch 10 video solutions for Different Ways to Add Parentheses, a medium level problem involving Math, String, Dynamic Programming. This walkthrough by NeetCode has 436,448 views views. Want to try solving it yourself? Practice on FleetCode or read the detailed text solution.
Given a string expression of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. You may return the answer in any order.
The test cases are generated such that the output values fit in a 32-bit integer and the number of different results does not exceed 104.
Example 1:
Input: expression = "2-1-1" Output: [0,2] Explanation: ((2-1)-1) = 0 (2-(1-1)) = 2
Example 2:
Input: expression = "2*3-4*5" Output: [-34,-14,-10,-10,10] Explanation: (2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10
Constraints:
1 <= expression.length <= 20expression consists of digits and the operator '+', '-', and '*'.[0, 99].'-' or '+' denoting the sign.Problem Overview: You receive a string expression containing numbers and the operators +, -, and *. The task is to compute every possible result produced by inserting parentheses in different ways. Each parenthesization changes the evaluation order, so you must explore all valid computation trees.
Approach 1: Divide and Conquer with Recursion (Time: O(n * 2^n), Space: O(2^n))
Treat every operator as a potential split point. Scan the expression and whenever you encounter an operator, split the string into left and right subexpressions. Recursively compute all results from the left part and all results from the right part, then combine them using the current operator. For example, if the left side produces values a and the right side produces b, compute a + b, a - b, or a * b depending on the operator. This approach naturally forms a recursion tree similar to building all binary expression trees. The number of combinations grows exponentially because each operator introduces new ways to group the expression.
Approach 2: Dynamic Programming with Memoization (Time: O(n * 2^n), Space: O(n * 2^n))
The recursive solution recomputes the same subexpressions multiple times. Memoization removes that redundancy. Store the computed results for every substring of the expression in a hash map keyed by the substring. When recursion encounters the same subexpression again, return the cached results instead of recomputing them. The algorithm still explores every valid partition of the expression, but repeated work disappears. This optimization drastically improves practical performance for longer expressions. Conceptually this is a top‑down dynamic programming strategy layered on top of a recursion divide‑and‑conquer structure, with cached subproblem results using memoization.
Recommended for interviews: Start with the divide-and-conquer explanation since it mirrors how the expression can be split at every operator. Then add memoization to avoid recomputing subexpressions. Interviewers expect the memoized version because it demonstrates recognition of overlapping subproblems and the ability to optimize recursive solutions.
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Divide and Conquer with Recursion | O(n * 2^n) | O(2^n) | Good for explaining the core idea of splitting expressions and building all evaluation trees |
| Dynamic Programming with Memoization | O(n * 2^n) | O(n * 2^n) | Preferred solution in interviews and production when repeated subexpressions appear |