🧠
Backtracking with Early Pruning and Long Operand Handling
💡 This approach improves brute force by pruning invalid operands early (leading zeros) and using long integers to avoid overflow, making it more robust and slightly faster.
Intuition
By skipping invalid operands early and carefully managing operand parsing, we reduce unnecessary recursion and avoid errors from integer overflow.
Algorithm
- At each recursion, skip operands with leading zeros immediately to avoid invalid expressions.
- Use 64-bit integers (long/long long) to parse operands to prevent overflow.
- Continue recursive exploration with the same logic as brute force but with these optimizations.
- This reduces the search space and prevents runtime errors.
💡 The key improvement is pruning invalid operands early and safe operand parsing, which are common interview pitfalls.
from typing import List
def addOperators(num: str, target: int) -> List[str]:
res = []
def backtrack(index: int, path: str, evaluated: int, last_operand: int):
if index == len(num):
if evaluated == target:
res.append(path)
return
for i in range(index, len(num)):
if i != index and num[index] == '0':
break # prune leading zero
curr_str = num[index:i+1]
curr = int(curr_str)
if index == 0:
backtrack(i+1, curr_str, curr, curr)
else:
backtrack(i+1, path + '+' + curr_str, evaluated + curr, curr)
backtrack(i+1, path + '-' + curr_str, evaluated - curr, -curr)
backtrack(i+1, path + '*' + curr_str, evaluated - last_operand + last_operand * curr, last_operand * curr)
backtrack(0, '', 0, 0)
return res
# Driver code
if __name__ == '__main__':
print(addOperators("105", 5))
print(addOperators("00", 0))
print(addOperators("3456237490", 9191))
Line Notes
if i != index and num[index] == '0':Prune operands with leading zeros early to avoid invalid expressions
curr = int(curr_str)Parse operand safely as integer; Python int can handle large numbers
if index == 0:First operand added without operator to start expression
backtrack(i+1, path + '*' + curr_str, evaluated - last_operand + last_operand * curr, last_operand * curr)Handle multiplication precedence by adjusting evaluated value
import java.util.*;
public class ExpressionAddOperators {
public List<String> addOperators(String num, int target) {
List<String> res = new ArrayList<>();
backtrack(res, new StringBuilder(), num, target, 0, 0L, 0L);
return res;
}
private void backtrack(List<String> res, StringBuilder path, String num, int target, int index, long evaluated, long lastOperand) {
if (index == num.length()) {
if (evaluated == target) {
res.add(path.toString());
}
return;
}
int len = path.length();
for (int i = index; i < num.length(); i++) {
if (i != index && num.charAt(index) == '0') break; // prune leading zero
String currStr = num.substring(index, i + 1);
long curr = Long.parseLong(currStr);
if (index == 0) {
path.append(currStr);
backtrack(res, path, num, target, i + 1, curr, curr);
path.setLength(len);
} else {
path.append('+').append(currStr);
backtrack(res, path, num, target, i + 1, evaluated + curr, curr);
path.setLength(len);
path.append('-').append(currStr);
backtrack(res, path, num, target, i + 1, evaluated - curr, -curr);
path.setLength(len);
path.append('*').append(currStr);
backtrack(res, path, num, target, i + 1, evaluated - lastOperand + lastOperand * curr, lastOperand * curr);
path.setLength(len);
}
}
}
// Driver code
public static void main(String[] args) {
ExpressionAddOperators solver = new ExpressionAddOperators();
System.out.println(solver.addOperators("105", 5));
System.out.println(solver.addOperators("00", 0));
System.out.println(solver.addOperators("3456237490", 9191));
}
}
Line Notes
if (i != index && num.charAt(index) == '0') break;Prune invalid operands with leading zeros early
long curr = Long.parseLong(currStr);Use long to safely parse large operands
if (index == 0) {First operand added without operator
path.setLength(len);Backtrack by resetting path length after recursion
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
vector<string> addOperators(string num, int target) {
vector<string> res;
string path;
backtrack(num, target, 0, 0, 0, path, res);
return res;
}
private:
void backtrack(const string& num, int target, int index, long long evaluated, long long lastOperand, string& path, vector<string>& res) {
if (index == num.size()) {
if (evaluated == target) {
res.push_back(path);
}
return;
}
int len = path.size();
for (int i = index; i < num.size(); ++i) {
if (i != index && num[index] == '0') break; // prune leading zero
string currStr = num.substr(index, i - index + 1);
long long curr = stoll(currStr);
if (index == 0) {
path += currStr;
backtrack(num, target, i + 1, curr, curr, path, res);
path.resize(len);
} else {
path += '+' + currStr;
backtrack(num, target, i + 1, evaluated + curr, curr, path, res);
path.resize(len);
path += '-' + currStr;
backtrack(num, target, i + 1, evaluated - curr, -curr, path, res);
path.resize(len);
path += '*' + currStr;
backtrack(num, target, i + 1, evaluated - lastOperand + lastOperand * curr, lastOperand * curr, path, res);
path.resize(len);
}
}
}
};
// Driver code
int main() {
Solution sol;
vector<string> res = sol.addOperators("105", 5);
for (auto& expr : res) cout << expr << endl;
cout << "---" << endl;
res = sol.addOperators("00", 0);
for (auto& expr : res) cout << expr << endl;
cout << "---" << endl;
res = sol.addOperators("3456237490", 9191);
for (auto& expr : res) cout << expr << endl;
return 0;
}
Line Notes
if (i != index && num[index] == '0') break;Prune operands with leading zeros early
long long curr = stoll(currStr);Use 64-bit integer to safely parse operands
if (index == 0) {First operand added without operator
path.resize(len);Backtrack by resetting path length after recursion
var addOperators = function(num, target) {
const res = [];
function backtrack(index, path, evaluated, lastOperand) {
if (index === num.length) {
if (evaluated === target) res.push(path);
return;
}
for (let i = index; i < num.length; i++) {
if (i !== index && num[index] === '0') break; // prune leading zero
const currStr = num.substring(index, i + 1);
const curr = Number(currStr);
if (index === 0) {
backtrack(i + 1, currStr, curr, curr);
} else {
backtrack(i + 1, path + '+' + currStr, evaluated + curr, curr);
backtrack(i + 1, path + '-' + currStr, evaluated - curr, -curr);
backtrack(i + 1, path + '*' + currStr, evaluated - lastOperand + lastOperand * curr, lastOperand * curr);
}
}
}
backtrack(0, '', 0, 0);
return res;
};
// Driver code
console.log(addOperators("105", 5));
console.log(addOperators("00", 0));
console.log(addOperators("3456237490", 9191));
Line Notes
if (i !== index && num[index] === '0') break;Prune invalid operands with leading zeros early
const curr = Number(currStr);Parse operand safely as number
if (index === 0) {First operand added without operator
backtrack(i + 1, path + '*' + currStr, evaluated - lastOperand + lastOperand * curr, lastOperand * curr);Handle multiplication precedence
TimeO(4^n) but with pruning reduces some branches
SpaceO(n) recursion stack and expression building
Pruning leading zeros cuts down invalid paths early, improving runtime but worst case remains exponential.
💡 Pruning can reduce millions of invalid paths, making the solution practical for typical interview inputs.
Interview Verdict: Accepted with better pruning
This approach is more robust and efficient than pure brute force, suitable for interviews.