[LintCode] Add Operators

Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not unary) +-, or * between the digits so they evaluate to the target value.

Example
"123", 6 -> ["1+2+3", "1*2*3"] 
"232", 8 -> ["2*3+2", "2+3*2"]
"105", 5 -> ["1*0+5","10-5"]
"00", 0 -> ["0+0", "0-0", "0*0"]
"3456237490", 9191 -> []
 
 
Logically we need to first enumerate numbers, then operators. We can either enumerate all possible integer lists first, then enumerate operators all each list, as shown in Solution 1.
Or better, we can combine them together: enumerate one number, then enumerate the binary operators that can stand in between the current enumerated number and its predecessor. 
The runtime of both solutions are the same since both needs to check all possible number combinations with all possible operators combination for each of the number combination. 
However, solution 1 needs to use extra memory to store all possible integer list combinations, whereas solution 2 does not use any extra memory if we don't consider the recursive stack 
memory usage.
 
We need to keep track of the current sum so that after we've used all digits in the given string we know if the current combinations sums to target. For addition and subtraction, this is easy 
as passing a current sum to the recursive dfs call is sufficient. For multiplication however, we need to save more info from the previous recursive call. 
Take the following as an example: 
given string "34562374", and we've already had 34 - 56 * 23  so far, the current sum is 34 - 56 * 23;
Say the next enumerated number is 74 and we decide to use a *  : 34 - 56 * 23 * 74. Since we can not use parentheses to affect the operators priority, we are in a dilemma. The current sum is 
34 - 56 * 23 and (34 - 56 * 23) * 74 is definitely not the same with 34 - 56 * 23 * 74. To fix this issue, we need to keep track of another variable at each recursive call level: the result of only multiplication operations from the previous call. I.e, We need to save -56 * 23 when processing 74. This way we can reconstruct the previous combinations correctly as shown in the following.
 
34 - 56 * 23 - (-56 * 23) + (-56 * 23)* 74
 
We call this extra bookkeeping info lastFactor. For + and -, lastFactor is simply the previously processed number; For *, lastFactor is the previous lastFactor * the current number.
The following summarizes the formula for +, -, *
 
+ or - :  currSum = currSum +(-) currNum;    lastFactor = +(-) lastFactor;
*:     currSum = currSum - lastFactor + lastFactor * currNum;    lastFactor = lastFactor * currNum;
 
 
There are also two corner cases that need to be considered:
1. operators can only exist in between numbers, there should be no operators in front of the first number, +3456 -23 is an invalid result.
2. Numbers should not have leading 0s, 034 as a number is an invalid enumeration.
 
 
Solution 1.
 1 public class Solution {
 2     public List<String> addOperators(String num, int target) {
 3         List<String> results = new ArrayList<String>();
 4         if(num == null || num.length() == 0) {
 5             return results;
 6         }
 7         List<List<Integer>> integerLists = new ArrayList<>();
 8         enumerateIntegerLists(integerLists, new ArrayList<Integer>(), num, 0);
 9         for(List<Integer> integers : integerLists) {
10             enumerateOperators(results, integers, new ArrayList<String>(), integers.get(0), integers.get(0), target, 1);
11         }
12         return results;
13     }
14     private void enumerateIntegerLists(List<List<Integer>> integerLists, List<Integer> integers,
15                                         String num, int startIdx) {
16         if(startIdx == num.length()) {
17             integerLists.add(new ArrayList<Integer>(integers));
18             return;
19         }
20         int currNum = num.charAt(startIdx) - '0';
21         integers.add(currNum);
22         enumerateIntegerLists(integerLists, integers, num, startIdx + 1);
23         integers.remove(integers.size() - 1);
24         if(currNum != 0){
25             for(int i = startIdx + 1; i < num.length(); i++) {
26                 currNum = currNum * 10 + num.charAt(i) - '0';
27                 integers.add(currNum);
28                 enumerateIntegerLists(integerLists, integers, num, i + 1);
29                 integers.remove(integers.size() - 1);
30             }
31         }
32     }
33     private void enumerateOperators(List<String> results, List<Integer> integers, List<String> operators, 
34                                     int currSum, int lastFactor, int target, int currIdx) {
35         if(currIdx == integers.size()) {
36             if(currSum == target) {
37                 results.add(constructFormula(integers, operators));
38             }
39             return;
40         }
41         int newSum = 0, newLastFactor = 0;
42         
43         operators.add("+");
44         newSum = currSum + integers.get(currIdx);
45         newLastFactor = integers.get(currIdx);
46         enumerateOperators(results, integers, operators, newSum, newLastFactor, target, currIdx + 1);
47         operators.remove(operators.size() -1);
48         
49         operators.add("-");
50         newSum = currSum - integers.get(currIdx);
51         newLastFactor = 0 - integers.get(currIdx);
52         enumerateOperators(results, integers, operators, newSum, newLastFactor, target, currIdx + 1);
53         operators.remove(operators.size() -1);
54         
55         operators.add("*");
56         newSum = currSum - lastFactor + lastFactor * integers.get(currIdx);
57         newLastFactor = lastFactor * integers.get(currIdx);
58         enumerateOperators(results, integers, operators, newSum, newLastFactor, target, currIdx + 1);
59         operators.remove(operators.size() -1);        
60     }
61     private String constructFormula(List<Integer> integers, List<String> operators) {
62         StringBuilder sb = new StringBuilder();
63         sb.append(integers.get(0));
64         for(int i = 0; i < operators.size(); i++) {
65             sb.append(operators.get(i));
66             sb.append(integers.get(i + 1));
67         }
68         return sb.toString();
69     }
70 }

 

 

Solution 2. 

The recursive call dfs() is the core here.

void dfs(int pos, String str, long sum, long lastF):

The output condition is that we've used all digits(pos == num.length()) and the current sum equals to target(sum == target).

The functionality of this dfs is: Given the partial result string, current Sum sum and lastFactor lastF from dfs to num[0.......pos - 1],

and starting from index pos, enumerate one number that starts at index pos, the do the following.

a. If this is the first number(pos == 0), then add this number to the string and make a recursive call starting from the next unused index.

b. If this is not the first number(pos != 0), then update current sum and lastFactor accordingly and make 3 recursive calls starting from the next unused index, each call is for one of the 3 operators.

As long as the number at starting index pos is not 0, we repeat a && b until we've reached the end of the digits string; This is done by line 13.

Otherwise, we know that if we include more digits we would have numbers with leading 0. Skip all the rest cases.

 

 1 public class Solution {
 2     String num;
 3     int target;
 4     List<String> ans = new ArrayList<>();
 5 
 6     void dfs(int pos, String str, long sum, long lastF) {
 7         if (pos == num.length()) {
 8             if (sum == target) {
 9                 ans.add(str);
10             }
11             return;
12         }
13         for (int i = pos; i < num.length(); i++) {
14             long cur = Long.parseLong(num.substring(pos, i + 1));
15 
16             if (pos == 0) {
17                 dfs(i + 1, "" + cur, cur, cur);
18             } else {
19                 dfs(i + 1, str + "*" + cur, sum - lastF + lastF * cur, lastF * cur);
20                 dfs(i + 1, str + "+" + cur, sum + cur, cur);
21                 dfs(i + 1, str + "-" + cur, sum - cur, -cur);
22             }
23             if (num.charAt(pos) == '0') {
24                 break;
25             }
26         }
27     }
28     public List<String> addOperators(String num, int target) {
29         this.num = num;
30         this.target = target;
31         dfs(0, "", 0, 0);
32         return ans;
33     }
34 }

 

 
Related Problems 
Combination Sum
Combination Sum II
Combinations
posted @ 2017-09-29 07:26  Review->Improve  阅读(256)  评论(0编辑  收藏  举报