原题链接:91. 解码方法
解题思路:
创建dp
数组的注意点:
- 根据题意,
s[0]
可能为'0'
或'1'
,我们可以简单推导出s[0]
的初始状态为:dp[1] = s[0] === '0' ? 0 : 1;
。 - 而任意一个位置
dp[i]
的状态,最多可能和前两个位置有关。 - 因此创建
s.length + 1
长度的dp
数组,并初始化dp[0] = 1
,保证可以向前查找两位的状态,也就是默认虚拟的s[-1]
位置是合规的编码。 - dp[i]对应的字符串位置为s[i - 1]。
该题可以拆解为以下几种场景
-
s[i - 2] + s[i - 1]
的范围是10~26
:- 编码为
10
或20
,只存在一种编码方法,并且只与dp[i - 2]
有关,状态转移方程:dp[i] = dp[i - 2];
。 - 编码为
11-19
或21-26
,当前状态与前两个状态有关,状态转移方程:dp[i] = dp[i - 1] + dp[i - 2];
。
- 编码为
-
s[i - 1]
为0,由于10
或20
已经处理过,此时s[i - 2] + s[i - 1]
只有30
、40
、50
等一系列可能性,当前无法解码,可直接返回0
。 -
s[i - 1]
为1-9
,此时不会产生新的编码方法,状态转移方程:dp[i] = dp[i - 1];
。
/**
* @param {string} s
* @return {number}
*/
var numDecodings = function(s) {
// 创建一个s.length + 1长度的数组递推,索引从1到s.length,对应s的每个位置
// dp[i]对应的字符串位置为s[i - 1]
let dp = new Array(s.length + 1).fill(0);
// 由于dp[i]的状态与dp[i - 1]和dp[i - 2]有关,而我们只能创建s[0]的初始状态
// 0位置设置为1,保证递推能有效进行。
dp[0] = 1;
// 创建s[0]的初始状态,为0时解码方法为0
dp[1] = s[0] === '0' ? 0 : 1;
// 从s[1]开始递推
for (let i = 2; i < dp.length; i++) {
// 临近两位的编码为10-26
if (s[i - 2] === '1' || (s[i - 2] === '2' && s[i - 1] <= '6')) {
// 如果当前编码为10和20,表示只能从i - 2位置编码而来,当前i - 1无解码方法
if (s[i - 1] === '0') {
dp[i] = dp[i - 2];
} else {
// 如果编码为11-19、21-26,可以由i - 1和i - 2位置编码而来
dp[i] = dp[i - 1] + dp[i - 2];
}
} else if (s[i - 1] === '0') {
// 当前编码为0,无法解码,返回0
// 10和20的编码已经处理过,此处不会包含这两个场景
return 0;
} else {
// 如果当前编码是1-9,则不会产生新的编码方法,数量与dp[i - 1]相等
dp[i] = dp[i - 1];
}
}
// 递推到数组最后位置,表示找到了解码方法数量
return dp[s.length];
};