{x}
blog image

Expression Add Operators

Given a string num that contains only digits and an integer target, return all possibilities to insert the binary operators '+', '-', and/or '*' between the digits of num so that the resultant expression evaluates to the target value.

Note that operands in the returned expressions should not contain leading zeros.

 

Example 1:

Input: num = "123", target = 6
Output: ["1*2*3","1+2+3"]
Explanation: Both "1*2*3" and "1+2+3" evaluate to 6.

Example 2:

Input: num = "232", target = 8
Output: ["2*3+2","2+3*2"]
Explanation: Both "2*3+2" and "2+3*2" evaluate to 8.

Example 3:

Input: num = "3456237490", target = 9191
Output: []
Explanation: There are no expressions that can be created from "3456237490" to evaluate to 9191.

 

Constraints:

  • 1 <= num.length <= 10
  • num consists of only digits.
  • -231 <= target <= 231 - 1

Solution Explanation for LeetCode 282. Expression Add Operators

This problem asks us to find all valid expressions formed by inserting '+', '-', and '*' operators between the digits of a given number string num such that the expression evaluates to a given target value.

Approach: Backtracking

The most efficient way to solve this problem is using backtracking. We explore all possible combinations of operators and operands recursively.

Algorithm:

  1. Base Case: If we've processed all digits in num, check if the current expression evaluates to the target. If it does, add the expression to the result list.

  2. Recursive Step: For each digit (or consecutive digits forming a number) starting from the current position:

    • Consider the current digit (or number) as an operand.
    • If it's not the first digit, explore three possibilities: add '+', '-', or '*' operator before the current operand. Update the current value of the expression accordingly. The multiplication case requires careful handling to account for the order of operations.
    • Recursively call the function for the remaining digits.
  3. Handling Leading Zeros: We need to ensure that operands don't start with '0' (except for '0' itself). This is handled by checking num[u] == '0' and breaking the loop if it's true and not the first digit of the current number.

Code (Python):

class Solution:
    def addOperators(self, num: str, target: int) -> List[str]:
        ans = []
 
        def dfs(u, prev, curr, path): # u: index, prev: previous operand, curr: current expression value, path:expression string
            if u == len(num):
                if curr == target:
                    ans.append(path)
                return
            for i in range(u, len(num)):
                if i != u and num[u] == '0':  #Handle leading zeros
                    break
                next = int(num[u : i + 1])
                if u == 0:
                    dfs(i + 1, next, next, path + str(next)) #First operand
                else:
                    dfs(i + 1, next, curr + next, path + "+" + str(next)) #Addition
                    dfs(i + 1, -next, curr - next, path + "-" + str(next)) #Subtraction
                    dfs(i + 1, prev * next, curr - prev + prev * next, path + "*" + str(next)) #Multiplication
 
        dfs(0, 0, 0, "")
        return ans

Code (Java):

class Solution {
    private List<String> ans;
    private String num;
    private int target;
 
    public List<String> addOperators(String num, int target) {
        ans = new ArrayList<>();
        this.num = num;
        this.target = target;
        dfs(0, 0L, 0L, ""); // Use long to handle potential overflow
        return ans;
    }
 
    private void dfs(int u, long prev, long curr, String path) {
        if (u == num.length()) {
            if (curr == target) ans.add(path);
            return;
        }
        for (int i = u; i < num.length(); i++) {
            if (i != u && num.charAt(u) == '0') break; //Handle leading zeros
            long next = Long.parseLong(num.substring(u, i + 1));
            if (u == 0) dfs(i + 1, next, next, String.valueOf(next));
            else {
                dfs(i + 1, next, curr + next, path + "+" + next);
                dfs(i + 1, -next, curr - next, path + "-" + next);
                dfs(i + 1, prev * next, curr - prev + prev * next, path + "*" + next);
            }
        }
    }
}

Time Complexity: O(4^N * N), where N is the length of num. The 4 comes from the four possibilities at each position (+, -, *, or nothing for the first digit). The N factor comes from the potential length of the numbers formed by consecutive digits. The complexity is not strictly O(4^N) because the number of recursive calls is affected by the actual digits and the target.

Space Complexity: O(N) for the recursion stack and to store the result list. The space used by the result list is proportional to the number of valid expressions, which can be at most O(4^N).

The C# solution provided in the original response is significantly different, it uses a dynamic programming approach with memoization. While this approach can be efficient in certain scenarios, it involves more complex data structures and could be harder to understand than a straightforward recursive backtracking solution. I recommend using backtracking as it is generally simpler to implement and comprehend for this problem.