1. 程式人生 > >leetcode282 - Expression Add Operators - hard

leetcode282 - Expression Add Operators - hard

bsp leetcode amp ive ont output 高頻 this tween

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 1:
Input: num = "123", target = 6
Output: ["1+2+3", "1*2*3"]
Example 2:
Input: num = "232", target = 8
Output: ["2*3+2", "2+3*2"]
Example 3:
Input: num = "105", target = 5
Output: ["1*0+5","10-5"]
Example 4:
Input: num = "00", target = 0
Output: ["0+0", "0-0", "0*0"]
Example 5:
Input: num = "3456237490", target = 9191
Output: []

DFS。
函數頭:private void dfs(int offset, long cal, long lastFact, String crt, List<String> ans)
遞歸定義:在中間的general狀態下,offset這個index以前的數字被加減乘分割的方式已經定好了放在crt裏,這種方式算出來的到目前的答案也定好了放在cal裏,你接下來隨便試offset和後面的數字分割的方式,等有一天試成功了你放到ans裏。對了同時傳一個額外信息lastFact,表示到目前為止最後一個被+-的因數,給你用來輔助現在嘗試*用。
遞歸拆分:這題能產生不同組合無非依賴於1.長數字怎麽被分割為好幾個小數字,2.分割點插的什麽二元操作符。一次dfs內:首先for循環看substring要從offset開始停到哪裏來取數字產生下一個因數。有了這個因數後看看如果拿前面的結果+-*這個新因數後,會怎麽更新cal和lastFact,從而進一步遞歸。
遞歸出口:當offset指不到新數字後你必須走了。如果該退場的時候發現誒我正好算出來的答案合格了,那把你現在找到的組合方式crt存進ans裏。

細節:
1.本題難點在於解決插入乘法符號*時怎麽快速更新計算結果。比如輸入為1234。在某一狀態,前面已經拼成了12+3,我們當前cal記下15,現在需要我們拼上新數字4。要是填+,更新結果很容易就加上去就好;如果要填*,從12+3變成12+3*4,我們不能只依靠上一個cal信息為15來快速得到答案,因為現在3不是先和12組合了而是先和4。如果記錄了上一個因子lastFact的話事情就簡單很多。先把lastFact從cal中減去得到上上次的答案12,再讓lastFact先和當前數字乘了得到3*4,再加回上上次的結果去。所以乘法時,cal更新為cal - lastFact + lastFact * crtFact,lastFact更新為lastFact * crtFact。
2.中間答案和因數都用Long存儲不要用int。因為很可能你兩個數一乘就超int了,但有時候減一減又可以拿到最後int的target,不能在中途把它們犧牲掉。
3.註意0的corner case。0不可以和其他數組成共同的因子。如果是01234開始繼續分解,第一個0只可以自己獨立做因子,不可以和後面的拉幫結派組成01,012什麽的。所以直接用parseLong還有缺陷,比如你不額外處理的話,000 湊0, parseLong看到00也讀成0,就會給你產生00+0的不合理結果。
4.所有數,甚至第一個數,都可以直接霸占到最後。比如12345,不是說一定要加符號進去從而第一個數最多到1234這樣,如果num = “12345” target = 12345,它自身就成立了,不需要加符號。
5.註意遞歸出口時的必須走了的“必須”。就是說你就算試出來的答案是錯的也要走,不可以繼續跑下面的代碼。本題湊巧下面代碼只有for循環,而且這個for循環在offset跑到最後的時候不會進去,所以不在前面寫return也沒關系。但寫其他dfs的時候還是要小心,盡量check一下最前面出口那裏要不要先寫return以避免編譯錯誤。

實現:

class Solution {
        private String num;
        private long target;
        private List<String> ans;

        public List<String> addOperators(String num, int target) {
            this.num = num;
            this.target = target;
            this.ans = new ArrayList<>();
            dfs(0, 0, 0, "");
            return ans;
        }

        private void dfs(int offset, long cal, long lastFact, String crt) {

            if (offset == num.length() && cal == target) {
                ans.add(crt);
            }
            
            // P2: 數可以直接霸占到最後,甚至第一個數。比如12345,不是說一定要加符號進去從而第一個數最多到1234這樣,如果target也是12345它自身就成立了。
            for (int i = offset; i < num.length(); i++) {
                long fact = Long.parseLong(num.substring(offset, i + 1));
                if (offset == 0) {
                    dfs(i + 1, cal + fact, fact, crt + num.substring(offset, i + 1));
                } else {
                    dfs(i + 1, cal + fact, fact, crt + "+" + fact);
                    dfs(i + 1, cal - fact, -fact, crt + "-" + fact);
                    dfs(i + 1, cal - lastFact + lastFact * fact, lastFact * fact, crt + "*" + fact);    
                }
                // P1: 如果是01234開始繼續分解,第一個0只可以自己獨立做因子,不可以和後面的拉幫結派組成01,012什麽的。所有直接用parseLong還有缺陷。
                if (fact == 0) {
                    break;
                }
            }
        }
    }

九章實現:

/**
* 本參考程序來自九章算法,由 @老頑童 提供。版權所有,轉發請註明出處。
* - 九章算法致力於幫助更多中國人找到好的工作,教師團隊均來自矽谷和國內的一線大公司在職工程師。
* - 現有的面試培訓課程包括:九章算法班,系統設計班,算法強化班,Java入門與基礎算法班,Android 項目實戰班,
* - Big Data 項目實戰班,算法面試高頻題班, 動態規劃專題班
* - 更多詳情請見官方網站:http://www.jiuzhang.com/?source=code
*/ 

public class Solution {
    /**
     * @param num    a string contains only digits 0-9
     * @param target an integer
     * @return return all possibilities
     */
    
    void dfs(String num, int target, int start, String str, long sum, long lastF, List<String> ans) {
        if (start == num.length()) {
            if (sum == target) {
                ans.add(str);
            }
            return;
        }
        for (int i = start; i < num.length(); i++) {
            long x = Long.parseLong(num.substring(start, i + 1));

            if (start == 0) {
                dfs(num, target, i + 1, "" + x, x, x, ans);
            } else {
                dfs(num, target, i + 1, str + "*" + x, sum - lastF + lastF * x, lastF * x, ans);
                dfs(num, target, i + 1, str + "+" + x, sum + x, x, ans);
                dfs(num, target, i + 1, str + "-" + x, sum - x, -x, ans);
            }
            if (x == 0) {
                break;
            }
        }
    }

    public List<String> addOperators(String num, int target) {
        // Write your code here
        List<String> ans = new ArrayList<>();
        dfs(num, target, 0, "", 0, 0, ans);
        return ans;
    }
}

leetcode282 - Expression Add Operators - hard