91. 解码方法(中等)
-
思路
-
这其实是一道字符串类的动态规划题,不难发现对于字符串s的某个位置i而言,我们只关心「位置 i 自己能否形成独立 item」和「位置 i 能够与上一位置(i-1)能否形成item 」,而不关心i-1 之前的位置。
-
有了以上分析,我们可以从前往后处理字符串s,使用一个数组记录以字符串s的每一位作为结尾的解码方案数。即定义
dp[i]
为考虑前i个字符的解码方案数
。将当前遍历到的字符记为cur
, 前一个字符记为prev
; -
对于字符串 s 的任意位置 i 而言,其存在三种情况:
- 只能由位置 i 的单独作为一个 item,转移的前提是
cur > 6 && (prev != 1 || prev != 2)
,转移逻辑为dp[i] = dp[i-1];
。 - 只能由位置 i 的与前一位置(i-1)共同作为一个 item,这里只有两种情况,如
10 / 20
, 转移的前提是cur == 0 && (prev == 1 || prev == 2)
,转移逻辑为dp[i] = dp[i-1];
- 位置 i 既能作为独立 item 也能与上一位置形成 item,转移的前提是
cur <= 6 && (prev == 1 || prev == 2)
,转移逻辑为dp[i] = dp[i-1] + dp[i-2];
- 只能由位置 i 的单独作为一个 item,转移的前提是
-
当然,前导 0 的情况需要另外考虑,详见代码。
-
-
代码
class Solution { public: int numDecodings(string s) { int n = s.size(); if(n == 0 || s[0] == '0') return 0; if(n == 1) return 1; vector dp(n+1, 1); int prev = s[0] - '0'; for(int i=2; i<=n; ++i){ int cur = s[i-1] - '0'; if(cur == 0 && (prev == 0 || prev > 2)) return 0; if(prev == 1 || prev == 2 && cur < 7){ if(cur == 0){ // 10 / 20 单独考虑 dp[i] = dp[i-2]; } else{ // 既可以单独成为一种情况 又可以和前面数字一起考虑 dp[i] = dp[i-1] + dp[i-2]; } } else{ // 只能单独成为一种情况 dp[i] = dp[i-1]; } prev = cur; } return dp[n]; } };
-
收获
- 看了题解不算难,但是要考虑到每一种情况。我一开始的想法是先分割成若干个字符串,然后判断每一个字符串所有可能的解码方式,最后累乘,但是当时没想到要逐位考虑,感觉不至于这么繁琐,果然以后想到怎么做就先试着做一下。
- 不过,完全不需要提前分割,在遍历的过程中也就完成了分割。