1. 题目解析
题目链接:91. 解码方法
这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。
2.算法原理
这是一道类似斐波那契数列的题目~
当我们遇到一个类似斐波那契数列的问题时,我们通常会想到使用动态规划(DP)来求解。动态规划是一种将复杂问题分解为简单子问题,并保存子问题解以便复用的算法思想。在解码字符串的问题中,我们同样可以采用这种方法。
1. 状态定义
首先,我们需要明确问题的状态。在这里,我们定义dp[i]
为字符串中从第0个字符到第i个字符这一段区间上,所有可能的解码方法的数量。这样,当我们求出dp[n]
(其中n是字符串的长度)时,就得到了整个字符串的解码方法数。
2. 状态转移
接下来,我们要分析dp[i]
是如何从前面的状态转移过来的。考虑到第i个字符,我们有两种可能的解码方式:
- 单独解码第i个字符:当第i个字符是'1'到'9'之间的数字时,它可以单独解码成一个字母。此时,字符串从第0个字符到第i个字符的解码方法数就等于从第0个字符到第i-1个字符的解码方法数,因为我们可以将前面所有的解码方法都加上这个单独的字符。即
dp[i] += dp[i-1]
。 - 与前一个字符结合解码:如果第i-1个字符和第i个字符组合起来在'10'到'26'之间(表示一个有效的两位数字),那么它们可以合并解码成一个字母。此时,字符串从第0个字符到第i个字符的解码方法数就等于从第0个字符到第i-2个字符的解码方法数,因为我们可以将前面所有的解码方法都加上这个两位数字。即
dp[i] += dp[i-2]
。
需要注意的是,如果第i个字符是'0',那么它不能单独解码成一个字母,必须与前一个字符结合。如果结合后的数字不在'10'到'26'之间,那么这段字符串就无法解码,此时dp[i]
应该保持为0。
3. 初始化
在开始计算之前,我们需要对dp
数组进行初始化。由于我们的状态转移依赖于dp[i-1]
和dp[i-2]
,所以至少需要初始化前两个位置的值。
- 对于
dp[0]
:如果字符串的第一个字符是'0',那么整个字符串无法解码,dp[0]
应该为0;否则,至少有一种解码方法(即单独解码第一个字符),dp[0]
应该为1。 - 对于
dp[1]
:我们需要考虑第一个和第二个字符的组合。如果第二个字符是'1'到'9',那么它可以单独解码,此时dp[1]
应该加上dp[0]
;如果第一个字符和第二个字符组合起来是一个有效的两位数字,那么也应该有一种解码方法,此时dp[1]
应该再加1。
3.代码编写
class Solution
{
public:
int numDecodings(string s)
{
int n = s.size();
vector<int> dp(n);
dp[0] = s[0] != '0';
if(n == 1) return dp[0];
if(s[0] != '0' && s[1] != '0') dp[1]++;
int t = (s[0] - '0') * 10 + (s[1] - '0');
if(t >= 10 && t <= 26) dp[1]++;
for(int i = 2; i < n; i++)
{
if(s[i] != '0') dp[i] += dp[i - 1];
int m = (s[i - 1] - '0') * 10 + (s[i] - '0');
if(m >= 10 && m <= 26) dp[i] += dp[i - 2];
}
return dp[n - 1];
}
};
The Last
嗯,就是这样啦,文章到这里就结束啦,真心感谢你花时间来读。
觉得有点收获的话,不妨给我点个赞吧!
如果发现文章有啥漏洞或错误的地方,欢迎私信我或者在评论里提醒一声~