Expression Add Operators

Updated on 26 May, 2025
Expression Add Operators header image

Problem Statement

The task is to generate all possible expressions by inserting the binary operators '+' (addition), '-' (subtraction), and '*' (multiplication) between the digits of a given numeric string num. These expressions, when evaluated, should result in a value equal to the specified target integer. It's crucial that these generated numeric expressions do not begin with any zeros unless the zero itself is the only digit, adhering to standard numeric representations.

Examples

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

Approach and Intuition

To solve this problem, we can use a recursive approach with backtracking to explore all possible ways to insert the operators:

  1. Initialization: Start with an empty expression and the first character of num. Progressively add each character to the expression.
  2. Recursive Exploration:
    • At every digit in num, decide whether to:
      • Insert a '+' and recursively process the remaining characters.
      • Insert a '-' and recursively process the remaining characters.
      • Insert a '*' to perform multiplication and recursively process the remaining characters.
      • Simply extend the current numeric value by appending the next digit (carefully considering not to start any new number with zero unless it's the only digit).
  3. Evaluation and Backtracking:
    • Constantly evaluate the expression as we extend it.
    • If at any point the partial expression matches the target (when all characters have been processed), add it to the result list.
    • If an expression doesn't work out, backtrack to explore the next possible set of operations.
  4. Base and Edge Cases:
    • If num starts with '0', only '0' can be used before an operator must be inserted (to avoid leading zeros in any number).
    • Continuously handle calculations that might involve large numbers or potential integer overflows, especially with multiple multiplications.

The use of recursive backtracking is efficient here due to the constraint that the length of num is at most 10, but being cautious with calculations is critical because of the possibility of large integer values and the significant variations in expressions that can be formed. This approach ensures all potential expressions are considered, and those meeting the target requirement are captured.

Solutions

  • Java
  • Python
java
class Solution {

  public ArrayList<String> results;
  public String numberSequence;
  public long goal;

  public void search(
      int pos, long prevOperand, long currOperand, long accumulated, ArrayList<String> operations) {
    String digits = this.numberSequence;

    // Processing completion check
    if (pos == digits.length()) {

      // Check if current total equals goal and no pending operations
      if (accumulated == this.goal && currOperand == 0) {
        StringBuilder expression = new StringBuilder();
        operations.subList(1, operations.size()).forEach(v -> expression.append(v));
        this.results.add(expression.toString());
      }
      return;
    }

    // Append current digit to operand
    currOperand = currOperand * 10 + Character.getNumericValue(digits.charAt(pos));
    String currValStr = Long.toString(currOperand);

    // Skip invalid leading zero cases
    if (currOperand > 0) {

      // Continue without adding operators
      search(pos + 1, prevOperand, currOperand, accumulated, operations);
    }

    // Addition operation
    operations.add("+");
    operations.add(currValStr);
    search(pos + 1, currOperand, 0, accumulated + currOperand, operations);
    operations.remove(operations.size() - 1);
    operations.remove(operations.size() - 1);

    if (operations.size() > 0) {

      // Subtraction operation
      operations.add("-");
      operations.add(currValStr);
      search(pos + 1, -currOperand, 0, accumulated - currOperand, operations);
      operations.remove(operations.size() - 1);
      operations.remove(operations.size() - 1);

      // Multiplication operation
      operations.add("*");
      operations.add(currValStr);
      search(
          pos + 1,
          currOperand * prevOperand,
          0,
          accumulated - prevOperand + (currOperand * prevOperand),
          operations);
      operations.remove(operations.size() - 1);
      operations.remove(operations.size() - 1);
    }
  }

  public List<String> addOperators(String num, int target) {

    if (num.length() == 0) {
      return new ArrayList<String>();
    }

    this.goal = target;
    this.numberSequence = num;
    this.results = new ArrayList<String>();
    this.search(0, 0, 0, 0, new ArrayList<String>());
    return this.results;
  }
}

The provided Java code defines a solution for the problem where one needs to insert mathematical operators ('+', '-', '*') into a string of digits to form valid arithmetic expressions that evaluate to a given target number.

  • The Solution class has several properties:

    • results to store valid expression strings.
    • numberSequence to store the input number as a string.
    • goal to store the target value.
  • The search method is a recursive function designed to try placing each of the mathematical operators or continuing without any operator between the digits:

    • It terminates the recursion when it reaches the end of the string (pos == digits.length()). If the current expression evaluates exactly to the goal without any pending calculations (currOperand == 0), it joins the components into a complete expression and adds it to results.
    • It handles numbers carefully to avoid expressions with leading zeros unless the number is zero itself.
    • It explores three possible paths by recursively calling itself:
      • Continuing the current operand by appending a digit.
      • Trying addition, subtraction, and multiplication with the current operand.
  • Error handling for expressions starting with zeros and handling of the precedence in operations, particularly with multiplication, are managed using the accumulated value and keeping track of the previous operand.

  • The addOperators method initializes the recursion:

    • It checks if the input number is empty.
    • Sets the goal and numberSequence.
    • Starts the recursive search from the beginning of the string.
  • Usage:

    • Construct an instance of Solution.
    • Invoke addOperators with a num string and a target integer to get a list of all valid expressions that result in the target.

This solution effectively constructs expressions using backtracking, ensuring that all possible combinations of operators are considered and processed efficiently.

python
class Solution:
    def evaluate_expression(self, digits: 'str', goal: 'int') -> 'List[str]':

        length = len(digits)
        results = []
        def backtrack(pos, previous, current, total, seq):

            # Processing finishes when end of string is reached
            if pos == length:
                
                # Check if the expression evaluates to goal
                if total == goal and current == 0:
                    results.append("".join(seq[1:]))  # skip the initial operator
                return

            # Build the current operand from the string
            current = current * 10 + int(digits[pos])
            operand_str = str(current)

            # Ignore leading zeros
            if current > 0:

                # Continue with building the number
                backtrack(pos + 1, previous, current, total, seq)

            # Addition
            seq.append('+')
            seq.append(operand_str)
            backtrack(pos + 1, current, 0, total + current, seq)
            seq.pop()
            seq.pop()

            # Only add subtraction and multiplication if there are operands formed before
            if seq:

                # Subtraction
                seq.append('-')
                seq.append(operand_str)
                backtrack(pos + 1, -current, 0, total - current, seq)
                seq.pop()
                seq.pop()

                # Multiplication
                seq.append('*')
                seq.append(operand_str)
                new_value = previous * current
                backtrack(pos + 1, new_value, 0, total - previous + new_value, seq)
                seq.pop()
                seq.pop()
        
        backtrack(0, 0, 0, 0, [])
        return results

This Python solution introduces a method to add operators ('+', '-', '*') between the digits of a string such that the expression equates to a specific target number. Here's a summary of how this solution works:

  • Define a class Solution containing the method evaluate_expression which takes two parameters: a string of digits and a goal integer.
  • Initialize an empty list called results to store valid expressions.
  • Define a nested function backtrack to handle the recursive construction of possible expressions. It takes parameters: the current position in the string (pos), the value of the last operand (previous), the current operand being built (current), the running total of the current expression (total), and the current sequence of characters (seq).
  • In backtrack, handle sequence termination when the end of the digits string is reached.
  • If the current built-up expression totaling to total equals goal, append the expression to results.
  • Recurse to form the current operand by extending it with the next digit to handle potential multi-digit operands.
  • Add the expression-separating operators only if there's no leading zero in the current number or if it's not the beginning of the sequence.
  • Explore three possible recursive paths from each position — one each for addition, subtraction, and multiplication — updating the sequence and total accordingly.
  • Start the backtracking process with base values for total, current, and previous operands.

Run the recursive backtrack function initially from position 0 of the string and with base values set to reflect an empty initial state, eventually returning the list of all valid combinations that meet the target expression's value. This approach ensures that all combinations of numbers and operators leading up to the target are considered.

Comments

No comments yet.