题目
leetcode 91.解码方法
In a brief: 将一串数字拆按不同方式拆分,但拆分后的个体的值的区间为1~26,求总拆分方法数。
思路
动态规划
定义
已知:字符串str[0…i…n]
dp[i] :对于str[0…i]的字符串,所得解码的总数
求dp[i]
首先分析str[i],可能值为:‘0’, ‘1’, ‘2’
解码总数取决于str[i]不同的取值,因此,对str[i]进行分类讨论:
i) str[i] == '0’
只有一个字符 '0’时,无法单独被解码。需要联系前面的一个字符。
当str[i - 1] == ‘1’ 或 '2’时,str[I - 1] + str[I] 为 ‘10’ 或 ‘20’,此时能唯一被解码。str[0…I-2]可能有不同的拆分方法,str[i - 1] + str[I] 只能放在一起组合。因此,str[0…i]的解码方法数与str[0…i-2]解码方法数一致,即 dp[i] = dp[i - 2]
ii)str[i] == ‘1’ 或 '2’
此时拆分方法有两种情况:
1: 单独对str[I]解码,则唯一被解码,dp[i] = dp[i -1]
2: 与前一项组合解码,则需要对str[i - 1]进行判断,只有当str[i - 1] == ‘1’ 或 '2’时,str[i] + str[i - 1] 才是符合题意的两位数,才能组合解码。
合并解码的数字能被唯一解码,则解码方法数与str[0…i-2]的解码方法数相同。即dp[i] = dp[i - 2]
若str[i - 1] == ‘1’ 或 ‘2’,此时既可以单独解码也可以组合解码, dp[i] = dp[i -1] + dp[i -2]
注意:当str[I - 1] = '2’时,str[I]的区间为[0,6]
对ii)中的1,2进行总结:
str[I - 1] == ‘1’ 或’2’时,可以组合或者单独解码,dp[i] = dp[i -1] + dp[i -2]
str[I - 1] =='0’时,只能单独解码,dp[i] = dp[i -1]
初始化
若str[0] != 0
dp[0] = 1
解题代码
本来代码是
class Solution {
public:
int numDecodings(string s) {
if(s[0] == '0')
return 0;
int n = s.size();
int dp[n];
dp[0] = 1;
for(int i = 1; i < n; i++) {
if(s[i] == '0') {
if(s[i - 1] == '1' || s[i - 1] == '2')
dp[i] = dp[i - 2];
else
return 0;
}
else {
if(s[i - 1] =='1' || s[i - 1] == '2' && s[i] > '0' && s[i] <= '6')//可以组合解码或者单独解码
dp[i] = dp[i - 1] + dp[i - 2];
else//只能单独解码
dp[i] = dp[i - 1];
}
}
return dp[n - 1];
}
};
但是在计算dp[1] = dp[1 - 1] + dp[1 - 2] = dp[0] + dp[-1]时,无法计算dp[-1],则将所有数往后移一位,即 i = i +1,变成dp[i + 1] = dp[i] + dp[i - 1],就能进行计算了。
AC代码
class Solution {
public:
int numDecodings(string s) {
if(s[0] == '0')
return 0;
int n = s.size();
int dp[n + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 1; i < n; i++) {
if(s[i] == '0') {
if(s[i - 1] == '1' || s[i - 1] == '2')
dp[i + 1] = dp[i - 1];
else
return 0;
}
else {
if(s[i - 1] =='1' || s[i - 1] == '2' && s[i] > '0' && s[i] <= '6')
dp[i + 1] = dp[i] + dp[i - 1];
else
dp[i + 1] = dp[i];
}
}
return dp[n];
}
};
注意:dp[1]在这里的意义才是str[0]的解码数
dp[0]是为了状态转移方程的计算而产生,也是为什么将i都向后移一位的原因。