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).
這個題想了好久,覺得真的是比較難想,好多case一遍一遍測試才通過。試着總結一下思路吧。
首先,假如我們分析到了中間,比較通用的情況。
現在在已有字符串的基礎上,新來一位字符,就要開始分情況。如果來的是'0',就只有唯一的組成情況,就是和前面的一個數字組成編碼,且前面的這個數字還必須是'1'或'2',否則就是無效的編碼。所以如果來的是'0',相當於要么和前面的數字組合,要么就無效。如果用動態規划的思想,那就先寫上偽代碼:
if (s[i] == '0') { if (s[i-1] == '1' || '2') dp[i] = dp[i-2]; else return 0; }
這里為什么是dp[i] = dp[i-2]呢?因為'0'只能作為和前面的一位字符組合存在,所以前面的字符出現的時候相當於並沒有出現,因為它要等着后面的'0'來和它組合,所以編碼種類和dp[i-2]一樣多。
接下來看如果來的是'1'~'9',不僅自己要再細分情況,也要再考慮前面一位的情況。如果前面一位是'1',或前一位是'2'且這一位是'1'~'6',那么可以產生新的編碼,因為它既可以獨立存在成一個字母,又可以和前面的數字組合形成一個字母。作為獨立存在的情況相當於就是取dp[i-1],因為添加一位獨立存在的字符並沒有增加任何可能性,所以保持和前狀態一樣;作為組合存在的情況同上,還要再往前想一位。因為如果組合存在的話,前面的一位其實也是和后面的一位作為組合存在而不是獨立存在,即當dp[i-1]那位字符來的時候,裝作它沒來,因為它要等着和后面的字符結合,所以編碼種類和dp[i-2]是一樣多的。所以最后dp[i] = dp[i-1] + dp[i-2]。
如果前面一位字符不是'1'或者'2',那么新來的字符位只能作為獨立存在,無法結合。所以並沒有產生新的編碼可能,即dp[i] = dp[i-1]。
總結起來偽代碼如下:
if (s[i-1] == '1' || (s[i-1] == '2' && '1' <= s[i] <= '6')) dp[i] = dp[i-1] + dp[i-2]; else dp[i] = dp[i-1];
這樣所有的情況就分析完了。現在看為了解決通用情況需要些什么,很明顯從上面來看,需要知道每一位字符前兩位,即dp[0]和dp[1]要首先知道。dp[0]很簡單,如果不是'0'那就是1,是'0'就直接返回0了,第一位是'0'無法解碼。dp[1]也並不復雜,其實和通用情況一樣,如果第二位是'0',dp[1]就是1或return 0,取決於能否和前面結合,即第一位是否是'1'或'2';如果第一位是'1',或第一位是'2'且第二位是'1'~'6',那dp[1]就是2,因為可以結合或獨立存在。其他情況就都是1了,因為只能獨立存在,不增加解碼可能性。
寫完了思路發現這道題就寫完了,把思路實現即為代碼。應該是有很多可以優化的地方,或者是思緒上,或者是代碼上,留作以后復習的時候優化吧。現在這么寫思路特別清楚,很容易理解,姑且先這樣吧!
Java
class Solution { public int numDecodings(String s) { if (s == "") return 0; char[] digit = s.toCharArray(); int[] dp = new int[digit.length]; //get dp[0] dp[0] = digit[0] == '0' ? 0 : 1; if (dp[0] == 0 || digit.length == 1) return dp[0]; //get dp[1] dp[1] = 1; if (digit[1] == '0') { if (digit[0] >= '3') return 0; } else if (digit[0] == '1' || (digit[0] == '2' && '1' <= digit[1] && digit[1] <= '6')) dp[1] = 2; //get dp[i] for (int i = 2; i < digit.length; i++) { if (digit[i] == '0') { if (digit[i-1] == '1' || digit[i-1] == '2') dp[i] = dp[i-2]; else return 0; } else if (digit[i-1] == '1' || (digit[i-1] == '2' && '1' <= digit[i] && digit[i] <= '6')) dp[i] = dp[i-1] + dp[i-2]; else dp[i] = dp[i-1]; } return dp[digit.length-1]; } }