A message containing letters from A-Z
is being encoded to numbers using the following mapping:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example,
Given encoded message "12"
, it could be decoded as "AB"
(1 2) or "L"
(12).
The number of ways decoding "12"
is 2.
思路:DP。这题依旧可以保持O(1)的空间复杂度。
先考虑字符串最普遍的情况,假设有字符串S1S2S3, 假设我们已经考虑到了S3,则S3有2种情况:
1)S2S3可以组成10-26, 设dp[2]为到S3位置所有可能的情况,则dp[2] = dp[0] + dp[1]。
先解释下这个公式为什么会成立,dp[0]为S1可能的解读情况,而当S2和S3各自单独解读为一个字母时,可能情况并没有增加,还是dp[0]。再考虑dp[1],它是截止到S2可能的解读情况,排除掉之前dp[0]时各自单独解读的情况,这里有两种可能:S1S2可以构成10-26,S3自己单独解读(可能情况数等于S1S2一起解读时的情况数),或者S2S3解读为10-26(可能情况数等于S2单独解读时的情况数),两者加起来正好是截止到S2的可能情况总数,即dp[1]。将上述情况进行综合得dp[2] = dp[0] + dp[1]。
2)S2S3不能组合成10-26, 则相当于在截止于S2的所有可能情况后面都额外加了一个无关紧要的S3,并没有增加额外的情况,因此dp[2] = dp[1]。
还有一点要注意的是,如果S3为0,则要么必需和S2组合到一起解读,要么整个字符串是非法的。因为单独一个0无法解读。因此,当S3为0时,dp[2] = dp[1]。而此时无法和S2组合,也就是非法的情况,我们在下面的实现方法中说明。
实现:我们维护两个值,preslow和prefast。
假设当前我们到了Si,则preslow为截止到Si-2时的情况数,prefast为截止到Si-1时的情况数。
先判断当前Si是否为0,若是,则令prefast = 0。
若Si能和Si-1构成10-26, 则领prefast = prefast + preslow;preslow = prefast - preslow。
这里可以看到,如果当前Si为0的话,prefast为0,则prefast=prefast + preslow将为preslow,即截止到si-2时的可能情况,与我们上面讨论的结果一致。否则就是dp[i -2] + dp[i - 1]。这样计算完之后,prefast已经更新到了截止于Si的值,因此preslow也往前移一位,得到之前Si-1的值。
如果Si不能和Si-1构成10-26,则dp[i] = dp[i-1],这里就是保持prefast值不变。而preslow仍要前移一位,值也要更新为prefast。这里要注意到,假如Si为0,则prefast当前已经为0,而在当前情况下这个字符串是非法的。经过这一步后,preslow也变成了0。所以之后不管怎么样,两个变量的值都将永远是0。
最后,prefast的值即为结果。
1 class Solution { 2 public: 3 int numDecodings(string s) { 4 int n = s.size(); 5 if (n < 1 || s[0] == '0') return 0; 6 int preslow = 1, prefast = 1; 7 for (int i = 1; i < n; i++) 8 { 9 if (s[i] == '0') prefast = 0; 10 if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] < '7')) 11 { 12 prefast = preslow + prefast; 13 preslow = prefast - preslow; 14 } 15 else preslow = prefast; 16 } 17 return prefast; 18 } 19 };