zoukankan      html  css  js  c++  java
  • [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
  • 相关阅读:
    关于白盒测试的心得
    基于Java的闰年测试
    等价类划分练习的代码实现
    软件测试中的等价类划分练习
    关于软件测试的初学小结
    现代软件工程作业第十二题(原十四题)
    好像木有白盒测试实验的报告,补一个~
    给大家推荐一本书啊啊~
    关于【做一名软件测试工程师,需要具备什么】的我的看法
    关于考试的笔记整理
  • 原文地址:https://www.cnblogs.com/lz87/p/7472392.html
Copyright © 2011-2022 走看看