LeetCode 91. Decode Ways

问题链接

LeetCode 91

题目解析

AZ对应数字126,给出一段数字串,求破译方法数。

解题思路

动态规划。关键在于分类,定义 (dp[i]) 为前i个字符的解密方法数,初始化为0。

小小注意:空数字串时返回0,非空时定义dp[0]=1,可以想象成没有也是一种解密方法(莫急待会再理解)。

对于某一数字 (s[i])

  • 单独解密:s[i] != 0,代表其可以单独解密(单数字→单字母),那么 (dp[i] += dp[i-1])
  • 复合解密:即俩数字→单字母。注意判断条件,s[i-1]非零且二者组成数字∈[10, 26],这种情况 (dp[i] += dp[i-2])

注意,二者不是if...else if,而是并列判断的,因为二者可共存。

实际操作时在原数字串前加了一个空字符,或许还是有些不懂。

举一个简单例子或许好一些。对于数字串1024:dp[0] = 1;首先判断数字1,dp[1] += dp[0] = 1;接着数字0,dp[2] += dp[1] = 1;然后判断数字2,dp[3] += dp[2] = 1;最后判断数字4,dp[4] += dp[3]; dp[4] += dp[2]; 结果为2。

你理解了为什么加一个前导空格以及 (dp[0] = 0) 了吗?

参考代码

class Solution {
public:
    int numDecodings(string s) {
        s = ' ' + s;
        int len = s.size();
        if (len <= 1) return 0;
        
        vector<int> dp(len);
        
        dp[0] = 1;
        for (int i = 1; i < len; i++) {
            dp[i] = 0;
            
            if (s[i] != '0')//单独
                dp[i] += dp[i-1];
            if (i-2 >= 0 && s[i-1] != '0' && (s[i-1]-'0')*10+(s[i]-'0')<=26)//联合
                dp[i] += dp[i-2];
        }
        
        return dp[len-1];
    }
};

改进

为了节省空间,可以将dp数字省去,只用两个变量a、b代表s[i-1]和s[i-2]的解码方法,思路相同,不断替换,也可得到答案。

从第二个字符(i=1)开始,判断当前字符如果为'0',说明当前字符不能单独解密,a=0。然后判断前一字符,如果前面的字符是1或者2时,即可以联合解密,则更新a = a + b,然后b = a - b,其实是将b赋值为之前的a;如果不满足这些条件的话,那么b = a。参考代码:

class Solution {
public:
    int numDecodings(string s) {
        if (s.empty() || s.front() == '0') return 0;
        int a = 1, b = 1;
        for (int i = 1; i < s.size(); i++) {
            if (s[i] == '0') a = 0;
            if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] <= '6')) {
                a = a + b;
                b = a- b;
            } else {
                b = a;
            }
        }
        return a;
    }
};

LeetCode All in One题解汇总(持续更新中...)

本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.


原文地址:https://www.cnblogs.com/AlvinZH/p/8534444.html