Overflow in intermediate arithmetic
Wrong move: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.
Move from brute-force thinking to an efficient approach using math strategy.
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 summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Math · Dynamic Programming
"2-1-1"
"2*3-4*5"
unique-binary-search-trees-ii)basic-calculator)expression-add-operators)the-score-of-students-solving-math-expression)minimize-result-by-adding-parentheses-to-expression)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #241: Different Ways to Add Parentheses
class Solution {
private static Map<String, List<Integer>> memo = new HashMap<>();
public List<Integer> diffWaysToCompute(String expression) {
return dfs(expression);
}
private List<Integer> dfs(String exp) {
if (memo.containsKey(exp)) {
return memo.get(exp);
}
List<Integer> ans = new ArrayList<>();
if (exp.length() < 3) {
ans.add(Integer.parseInt(exp));
return ans;
}
for (int i = 0; i < exp.length(); ++i) {
char c = exp.charAt(i);
if (c == '-' || c == '+' || c == '*') {
List<Integer> left = dfs(exp.substring(0, i));
List<Integer> right = dfs(exp.substring(i + 1));
for (int a : left) {
for (int b : right) {
if (c == '-') {
ans.add(a - b);
} else if (c == '+') {
ans.add(a + b);
} else {
ans.add(a * b);
}
}
}
}
}
memo.put(exp, ans);
return ans;
}
}
// Accepted solution for LeetCode #241: Different Ways to Add Parentheses
var memo = map[string][]int{}
func diffWaysToCompute(expression string) []int {
return dfs(expression)
}
func dfs(exp string) []int {
if v, ok := memo[exp]; ok {
return v
}
if len(exp) < 3 {
v, _ := strconv.Atoi(exp)
return []int{v}
}
ans := []int{}
for i, c := range exp {
if c == '-' || c == '+' || c == '*' {
left, right := dfs(exp[:i]), dfs(exp[i+1:])
for _, a := range left {
for _, b := range right {
if c == '-' {
ans = append(ans, a-b)
} else if c == '+' {
ans = append(ans, a+b)
} else {
ans = append(ans, a*b)
}
}
}
}
}
memo[exp] = ans
return ans
}
# Accepted solution for LeetCode #241: Different Ways to Add Parentheses
class Solution:
def diffWaysToCompute(self, expression: str) -> List[int]:
@cache
def dfs(exp):
if exp.isdigit():
return [int(exp)]
ans = []
for i, c in enumerate(exp):
if c in '-+*':
left, right = dfs(exp[:i]), dfs(exp[i + 1 :])
for a in left:
for b in right:
if c == '-':
ans.append(a - b)
elif c == '+':
ans.append(a + b)
else:
ans.append(a * b)
return ans
return dfs(expression)
// Accepted solution for LeetCode #241: Different Ways to Add Parentheses
struct Solution;
enum Tok {
Op(char),
Val(i32),
}
impl Solution {
fn diff_ways_to_compute(input: String) -> Vec<i32> {
let mut val = 0;
let mut toks: Vec<Tok> = vec![];
for c in input.chars() {
match c {
'+' | '-' | '*' => {
toks.push(Tok::Val(val));
toks.push(Tok::Op(c));
val = 0;
}
_ => {
val *= 10;
val += (c as u8 - b'0') as i32;
}
}
}
toks.push(Tok::Val(val));
Self::eval(&toks)
}
fn eval(toks: &[Tok]) -> Vec<i32> {
let mut res = vec![];
let n = toks.len();
for i in 0..n {
if let Tok::Op(c) = toks[i] {
let left = Self::eval(&toks[0..i]);
let right = Self::eval(&toks[i + 1..n]);
for l in &left {
for r in &right {
match c {
'+' => {
res.push(l + r);
}
'-' => {
res.push(l - r);
}
'*' => {
res.push(l * r);
}
_ => {}
}
}
}
}
}
if res.is_empty() {
if let Tok::Val(val) = toks[0] {
vec![val]
} else {
unreachable!()
}
} else {
res
}
}
}
#[test]
fn test() {
let input = "2-1-1".to_string();
let mut res = vec![0, 2];
let mut ans = Solution::diff_ways_to_compute(input);
res.sort_unstable();
ans.sort_unstable();
assert_eq!(ans, res);
let input = "2*3-4*5".to_string();
let mut res = vec![-34, -14, -10, -10, 10];
let mut ans = Solution::diff_ways_to_compute(input);
res.sort_unstable();
ans.sort_unstable();
assert_eq!(ans, res);
}
// Accepted solution for LeetCode #241: Different Ways to Add Parentheses
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #241: Different Ways to Add Parentheses
// class Solution {
// private static Map<String, List<Integer>> memo = new HashMap<>();
//
// public List<Integer> diffWaysToCompute(String expression) {
// return dfs(expression);
// }
//
// private List<Integer> dfs(String exp) {
// if (memo.containsKey(exp)) {
// return memo.get(exp);
// }
// List<Integer> ans = new ArrayList<>();
// if (exp.length() < 3) {
// ans.add(Integer.parseInt(exp));
// return ans;
// }
// for (int i = 0; i < exp.length(); ++i) {
// char c = exp.charAt(i);
// if (c == '-' || c == '+' || c == '*') {
// List<Integer> left = dfs(exp.substring(0, i));
// List<Integer> right = dfs(exp.substring(i + 1));
// for (int a : left) {
// for (int b : right) {
// if (c == '-') {
// ans.add(a - b);
// } else if (c == '+') {
// ans.add(a + b);
// } else {
// ans.add(a * b);
// }
// }
// }
// }
// }
// memo.put(exp, ans);
// return ans;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Pure recursion explores every possible choice at each step. With two choices per state (take or skip), the decision tree has 2ⁿ leaves. The recursion stack uses O(n) space. Many subproblems are recomputed exponentially many times.
Each cell in the DP table is computed exactly once from previously solved subproblems. The table dimensions determine both time and space. Look for the state variables — each unique combination of state values is one cell. Often a rolling array can reduce space by one dimension.
Review these before coding to avoid predictable interview regressions.
Wrong move: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.
Wrong move: An incomplete state merges distinct subproblems and caches incorrect answers.
Usually fails on: Correctness breaks on cases that differ only in hidden state.
Fix: Define state so each unique subproblem maps to one DP cell.