1. 程式人生 > >DP動態規劃專題一 :LeetCode 91. Decode Ways

DP動態規劃專題一 :LeetCode 91. Decode Ways

LeetCode 91. Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping:

‘A’ -> 1
‘B’ -> 2

‘Z’ -> 26
Given a non-empty string containing only digits, determine the total number of ways to decode it.

Example 1:

Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).
Example 2:

Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

解決動態規劃問題最主要的就是得到動態規劃的公式,思路還是用數學歸納法,先假設0~n-1已經完成,看看怎麼通過0 - n-1來推n,這個時候,如果n的答案至少依賴於n-1和n-2的答案時,資料結構就成了圖,除了分叉還有交叉,這時候就可以用hashmap儲存0 - n-1的答案,每次在後面需要時就可以直接得到。而hashmap又可以簡化為陣列的偽雜湊表來存放答案。這就是為什麼我們會看到有些題是用陣列來記錄的,好處在於陣列可以bulk load,優化時間。這道題的關鍵在於,怎麼更新這個可能的個數,先假設不存在0,那麼就需要判斷i與i-1的關係,如果i-1和i構成的數字小於等於26,那麼可能的個數是dp[i] = dp[i-1] + dp[i-2], 因為兩個數字可以結合一起decode,也可以分開decode;如果大於26,只能分開decode,dp[i] = dp[i-1],這樣就得到了公式。在此之上再考慮0的問題即可。
動態規劃公式:dp[n] = Math.max(composite <= 26 ? dp[n-2] + dp[n-1] : dp[n-1])

    public int numDecodings(String s) {
        //dp[n] = Math.max(composite <= 26 ? dp[n-2] + dp[n-1] : dp[n-1])
        if (s.length() == 0) return 0;
        if (s.charAt(0) == '0') return 0;
        int[] dp = new int[s.length() + 1];
        dp[0] = 1;
        dp[1] = 1;
        for (int i = 1; i < s.length(); i++) {
            int cur = (s.charAt(i-1) - '0') * 10 + s.charAt(i) - '0';
            if (s.charAt(i) == '0') {
                if (s.charAt(i-1) == '0' || cur > 26) return 0;
                dp[i+1] = dp[i-1];
                continue;
            }
            if (s.charAt(i-1) == '0') {
                dp[i+1] = dp[i];
                continue;
            }
            dp[i+1]  = cur > 26 ? dp[i] : dp[i] + dp[i-1];
        }
        return dp[s.length()];
    }

在正確的基礎上進行優化,由於n只和n-1和n-2有關,因此在更新的過程中,實際只有兩個值在發揮作用,因此我們可以省去陣列而只用兩個值進行更新。程式碼如下:

    public int numDecodings(String s) {
        //dp[n] = Math.max(composite <= 26 ? 2 * dp[n-2] : dp[n-2], dp[n-1])
        if (s.length() == 0) return 0;
        if (s.charAt(0) == '0') return 0;
        int i_2 = 1;
        int i_1 = 1;
        for (int i = 1; i < s.length(); i++) {
            int cur = (s.charAt(i-1) - '0') * 10 + s.charAt(i) - '0';
            int i_0 = 0;
            if (s.charAt(i) == '0') {
                if (s.charAt(i-1) == '0' || cur > 26) return 0;
                i_0 = i_2;
            } else if (s.charAt(i-1) == '0') {
                i_0 = i_1;
            } else {
                i_0 = cur > 26 ? i_1 : i_1 + i_2;
            }
            i_2 = i_1;
            i_1 = i_0;
        }
        return i_1;
    }