算法设计与分析第十三周——动态规划之Decode Ways
这周选择的题目比较简单,题目链接为:Decode Ways
题目详情
题目为给定一个只包含 ‘0’ - ‘9’的字符串s,并且给定一种解码格式:‘1’ -> 'A','2' -> 'B','3' -> 'C',...,‘26’ -> 'Z',问所给的字符串能够得到多少种解码结果。
样例分析:
输入 | 输出 | 解释 |
"12" | 2 | 结果1:‘1’、‘2’分开解码,分别解码为‘A’、‘B’ 结果2:‘12’被直接解码为‘L’ |
“226” | 3 | 结果1:分开"22"和"6"解码,解码为'V'、'F' 结果2:分开"2"和"26"解码,解码为'B'、'Z' 结果3:分开为"2"、"2"、"6",解码为'B'、'B'、'F' |
题目分析与算法设计
显而易见的,就是要我们找出给定字符串s中的所有字面数值大于0小于27的子串集合的个数,此时,从后往前遍历s,查找所有可能的情况,一次查找两位,如果这两位的字面数值满足大于0小于27,那么我们可以对这两位数中的后一位数计算得到的结果加上之前的结果数,即是从这两位数中的第一位数往后一直到s尾部的满足条件的子串集合的个数;如果不满足条件,那么只能向之前子串集合中加入当前遍历到的数,而不会产生多的子串集合。
比如,输入s为"2526",从后往前遍历:
- 遍历最后一个字符是只有集合 {'6'},结果为1,遍历到‘2’,因为查找的两位为"26",故集合为 {‘2’,'6'} 和 {"26"};
- 遍历到'5',查找的两位为"52",大于27了,说明"52"不能组在一起进行解码,而只能把'5'加入原有的就和中,形成 {‘2’,'6','5'} 和 {"26",'5'};
- 遍历到'2',查找的两位为"25",可以分别拆分成'2'、'5'或组合成"25"进行解码,那么,就形成了4个子串集合了,因为遍历到字符串后面的'2'时有两种情况,遍历到'5'时也有两种情况,相加即可。
那么,可以创建一个dp数组,长度为字符串长度加1,dp[ i ] 表示从下标 i 开始到 s 尾部的所有子串集合的个数,dp[s.size()]初始化为1,因为一个字符时只有一种结果,当然,如果 s[ s.size() - 1 ] 为'0',则dp[s.size() - 1] = 1,因为字符‘0’不能解码,由上述分析,可以很容易得到状态转移方程:
- dp[i] = dp[i+1] +dp[i+2] if s[i, i+2] > "00" and s[i, i+2] < "27";
- dp[i] = dp[i+1].
dp[0] 即为所求。
代码详情
int numDecodings(string s) {
int n = s.size();
int dp[n + 1] = {0};
dp[n] = 1;
if (s[n-1] != '0') {
dp[n-1] = 1;
}
for (int i = n - 2; i>= 0; i --) {
if (s[i] == '0') continue;
// if s[i, i + 2] < 27, we can get dp[i] by adding dp[i+1] and dp[i+2]
if (stoi(s.substr(i, 2)) <= 26) {
dp[i] = dp[i + 1] + dp[i + 2];
}
else {
dp[i] = dp[i + 1];
}
}
return dp[0];
}
代码只需一个循环,时间复杂度为O(n),只需维护一个dp数组,空间复杂度也为O(n)。
谢谢阅读。