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
  • 相关阅读:
    正负样本不平衡的处理
    基于贝叶斯优化的超参数搜索
    Axure RP的基础使用
    keil:C语言里面调用汇编程序
    keil的使用:新建Project
    汇编入门:循环语句的汇编实现
    转gif图
    LeetCode题解 #5 Longest Palindromic Substring
    Android 从本地图库或拍照后裁剪图片并设置头像
    Android使用简单的Service
  • 原文地址:https://www.cnblogs.com/lz87/p/7472392.html
Copyright © 2011-2022 走看看