LeetCode 91. Decode Ways
问题链接
题目解析
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和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.