刚吃完中饭,试了试外面的风力,还好我还比较胖风都动不了我。下面就和大家分享一下刚刷的这道题。
题目如下:
题意分析:
假如有一个由‘A-Z’字母构成的消息且可以通过'A' -> 1,'B' -> 2...'Z' -> 26的方式进行编码得到一组由数字构成的消息,现有一组由数字构成的消息,请将其解码成一个由‘A-Z’字母构成的消息,其中‘0’不可以解码,且最多只能对两位进行解码。
方法一(动态规划法)
本方法需要维护一个 dp 数组,其中 dp[i] 表示s中前i个字符组成的子串的解码方法个数(dp长度=输入字符串长度+1),并将 dp[0] 初始化为1。状态转移方程为 dp[i](前i个字符组成的子串的解码方法个数) = dp[i-1](前i-1个字符组成的子串的解码方法个数) + dp[i-2](前i-2个字符组成的子串的解码方法个数)。具体分析如下:若当前位置是0,那么一定不可以解码,即不能加上 dp[i-1],就只有看是否能与前一个数字组成大于等于 10 且小于等于 26 的数,若可以则可以加上 dp[i-2],否则 dp[i] 就只能为0。具体的操作如下:在遍历的过程中,对每个数字首先判断其是否为0,若是则将 dp[i] 赋为0,若不是则赋上 dp[i-1] 的值,然后看数组前一位是否存在,如果存在且满足前一位是1,或者与当前位一起组成的两位数不大于 26,则当前 dp[i] 值加上 dp[i - 2]。最后返回 dp 数组的最后一个值即可。
解题代码如下:
class Solution{
public:
int numDecodings(string s){
if ( s.size() == 0 || s[0] == '0') return 0;
vector<int> dp( s.size() + 1, 0 );
dp[0] = 1;
for (int i = 1; i < dp.size(); i++) {
//若当前数字为0,则无法解码,故只有0种解码方法
if ( s[i-1] == '0' ) dp[i] = 0;
//若当前数字不为0,则至少可以有与 dp[i-1] 一样多的解码方法
else dp[i] = dp[i-1];
//若当前数字能与前一个数字组成大于等于 10 且小于等于 26 的数,则可以有 dp[i-1] + dp[i-2] 种解码方法
if ( i > 1 && (s[i-2] == '1' || ( s[i-2] == '2' && s[i-1] <= '6' )) ) dp[i] += dp[i-2];
}
return dp.back();
}
};
提交后的结果如下:
方法二(优化方法一)
与方法一的思路大体一致,但是通过优化了方法一中的一些逻辑,减少了代码量。
注:substr()函数取到的是字符串,所以比较的对象不能是单个字符。
解题代码如下:
class Solution{
public:
int numDecodings(string s){
if ( s.size() == 0 || s[0] == '0') return 0;
vector<int> dp( s.size() + 1, 0 );
dp[0] = 1;
for (int i = 1; i < dp.size(); i++) {
//若当前数字不为0,则至少可以有跟 dp[i-1] 一样多的拆分情况
if ( s[i-1] != '0' ) dp[i] = dp[i-1];
//若当前数字能与前一个数字组成大于等于 10 且小于等于 26 的数,则可以有 dp[i-1] + dp[i-2] 种解码方法
if ( i > 1 && (s.substr(i-2, 2) <= "26") && (s.substr(i-2, 2) >= "10")) dp[i] += dp[i-2];
}
return dp.back();
}
};
提交后的结果如下:
日积月累,与君共进,增增小结,未完待续。