题目描述
一个只包含 A-Z
的消息可以用如下方式编码成数字:
'A' -> 1
'B' -> 2
...
'Z' -> 26
给定一个只包含数字的非空字符串,返回共有多少种解码方案。
样例1
输入:"12"
输出:2
解释:它可以被解码成 "AB" (1 2) 或者 "L" (12)。
样例2
输入:"226"
输出:3
解释:它可以被解码成 "BZ" (2 26), "VF" (22 6),
或者 "BBF" (2 2 6)。
算法
(动态规划) $O(n)$
这道题目可以用动态规划来做。
状态表示:$f[i]$ 表示前 $i$ 个数字共有多少种解码方式。
初始化:0个数字解码的方案数1,即 $f[0] = 1$。
状态转移:$f[i]$ 可以表示成如下两部分的和:
- 如果第 $i$ 个数字不是0,则 $i$ 个数字可以单独解码成一个字母,此时的方案数等于用前 $i - 1$ 个数字解码的方案数,即 $f[i - 1]$;
- 如果第 $i - 1$个数字和第 $i$ 个数字组成的两位数在 $10$ 到 $26$ 之间,则可以将这两位数字解码成一个字符,此时的方案数等于用前 $i - 2$ 个数字解码的方案数,即 $f[i - 2]$;
时间复杂度分析:状态数是 $n$ 个,状态转移的时间复杂度是 $O(1)$,所以总时间复杂度是 $O(n)$。
C++ 代码
class Solution {
public:
int numDecodings(string s) {
if (s.empty()) return 0;
int n = s.size();
vector<int> f(n + 1);
f[0] = 1;
for (int i = 1; i <= n; i ++ )
{
if (s[i - 1] < '0' || s[i - 1] > '9')
return 0;
f[i] = 0;
if (s[i - 1] != '0') f[i] = f[i - 1];
if (i > 1)
{
int t = (s[i-2]-'0')*10+s[i-1]-'0';
if (t >= 10 && t <= 26)
f[i] += f[i - 2];
}
}
return f[n];
}
};
怎么才能更好地理解这种DP分法能够实现“不重”?f[i-1]和f[i] 定义的规划路径没有重复吗?会不会出现f[i-1]里面的分发在f[i]也有?
0个数字解码的方案数为什么会为1呀,我怎么感觉f[0]就是凑数的呢
0个字符解码成0个数字可以看成一种方案。或者可以直接特殊计算一下
f[1]
和f[2]
的值,这样会更容易理解。f[i]
表示前i
个字母有多少种方案,那么f[i]
无论是字母还是数字至少有一种方案啊,为啥for 循环里面 f[i] = 0
而不是f[i] = 1
呢?;f[i]
在后面的if
语句里会被更新啊,另外前i
个字母不一定有合法方案,比如000
这个字符串的方案数就是0
~