【回溯】A064_LC_口算難題(暴搜 / 預處理權值)

一、Problem

Given an equation, represented by words on left side and the result on right side.

You need to check if the equation is solvable under the following rules:

Each character is decoded as one digit (0 - 9).
Every pair of different characters they must map to different digits.
Each words[i] and result are decoded as one number without leading zeros.
Sum of numbers on left side (words) will equal to the number on right side (result).
Return True if the equation is solvable otherwise return False.

Input: words = ["SEND","MORE"], result = "MONEY"
Output: true
Explanation: Map 'S'-> 9, 'E'->5, 'N'->6, 'D'->7, 'M'->1, 'O'->0, 'R'->8, 'Y'->'2'
Such that: "SEND" + "MORE" = "MONEY" ,  9567 + 1085 = 10652

Constraints:

2 <= words.length <= 5
1 <= words[i].length, result.length <= 7
words[i], result contains only upper case English letters.
Number of different characters used on the expression is at most 10.

二、Solution

樣例想表達的算式也就是這個樣子…

  SEND
+ MORE
-------
 MONEY

方法一:回溯

因爲 words 的長度最大也只有 5 個,word 和 res 最長也就 7,35 × 3 628 800 = 127008000,一億多的計算量。

  • 因爲每個 word 和 res 中的字符可能存在重複,但字符對應的數字一旦選擇是不會變的,所以需要去重。
  • word 和 res 的第一個字符都不能用 0 代表,所以需要用 bool 數組 notZero 標記一下開頭的字符是哪個。
  • 最後就是回溯選數了,並且用一個容器使當前字符與所選數字形成映射。

高運行時間飄過,

class Solution {
    String res, ws[];
    int resLen, m[];
    List<Character> alps;
    boolean fini, notZero[], v[];

    boolean isMatch() {
        int wSum = 0;
        for (String w : ws) {
            int t = 0;
            for (char c : w.toCharArray()) 
                t = t * 10 + m[c-'A'];
            wSum += t;
        }
        int rSum = 0;
        for (char c : res.toCharArray()) {
            rSum = rSum * 10 + m[c-'A'];
        }
        return wSum == rSum;
    }
    void dfs(int idx) {
        if (idx == alps.size()) {
            if (isMatch()) fini = true;
            return;
        }
        if (fini) return;
        
        char c = alps.get(idx);
        for (int i = 0; i < 10; i++) {
            if ((i == 0 && notZero[c-'A']) || v[i])
                continue;
            m[c - 'A'] = i;
            v[i] = true;
            dfs(idx+1);
            v[i] = false;
        }
    }
    public boolean isSolvable(String[] words, String res) {
        this.res = res;
        this.ws = words;
        notZero = new boolean[26];
        v = new boolean[10];
        m = new int[26];        //裝選中的字符
        Set<Character> st = new HashSet<>();
        
        for (String w : ws)
        for (int i = 0; i < w.length(); i++) {
            if (i == 0) 
                notZero[w.charAt(i)-'A'] = true;
            st.add(w.charAt(i));
        }
        for (int i = 0; i < res.length(); i++) {
            if (i == 0) 
                notZero[res.charAt(i)-'A'] = true;
            st.add(res.charAt(i));
        }
        alps = new ArrayList<>(st);
        dfs(0);
        return fini;
    }
}

複雜度分析

  • 時間複雜度:O(10!)O(10!)
  • 空間複雜度:O(n)O(n)

方法二:暴力優化

參考大神的優化思路:在判斷選出來的數字代入到 word 和 res 中檢查是否匹配的方法 isMatch 可以用空間換取時間。

待修正…

class Solution {
    String res, ws[];
    int resLen, m[], wl[], wr[];
    boolean fini, notZero[], v[];

    void calcWeight(String word, boolean isL) {
        int w = 1;
        if (isL) for (int i = word.length()-1; i >= 0; i--) {
            wl[word.charAt(i)-'A'] += w;
            w *= 10;
        }
        else for (int i = word.length()-1; i >= 0; i--) {
            wr[word.charAt(i)-'A'] += w;
            w *= 10;
        }
    }
    void dfs(int cur, int n, int l, int r) {
        if (cur > n) {
            if (l == r)
                fini = true;
            return;
        }
        if (fini)
            return;
        for (int i = 0; i < 10; i++) {
            if ((i == 0 && notZero[cur]) || v[i])
                continue;
            v[i] = true;
            dfs(cur+1, n, l + i * wl[i], r + i * wr[i]);
            v[i] = false;
        }
    }
    public boolean isSolvable(String[] words, String res) {
        this.res = res;
        this.ws = words;
        notZero = new boolean[26];
        v = new boolean[10];
        m = new int[26];        //裝選中的字符
        wl = new int[26];
        wr = new int[26];
        int num = 0;

        for (String w : ws) for (int i = 0; i < w.length(); i++) {
            int j = w.charAt(i)-'A';
            if (m[j] == 0)  m[j] = ++num;
            if (i == 0) notZero[m[j]] = false;
        }
        for (int i = 0; i < res.length(); i++) {
            int j = res.charAt(i)-'A';
            if (m[j] == 0) m[j] = ++num;
            if (i == 0) notZero[m[j]] = false;
        }
        for (String w : ws)
            calcWeight(w, true);
        calcWeight(res, false);
        dfs(1, num, 0, 0);
        return fini;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章