一、题目概述
二、思路方向
这个问题是一个典型的动态规划问题,其中我们可以使用一个数组来存储到达每个位置时的解码方法的总数。
我们定义一个数组
dp
,其中dp[i]
表示字符串s
的前i
个字符(从索引 0 到 i-1)的解码方法总数。初始化:
dp[0]
的值取决于字符串的第一个字符。如果第一个字符是 '0',则没有解码方法,dp[0] = 0
;否则,至少有一种解码方法(即第一个字符单独解码),dp[0] = 1
。状态转移:
对于每个位置i
(从 1 开始到字符串长度减一),我们考虑两种情况:
- 如果
s[i-1]
(即当前字符)不是 '0',那么它可以单独解码为一个字母,此时dp[i]
至少等于dp[i-1]
(即,在dp[i-1]
的基础上,加上当前字符的解码方式)。- 如果
s[i-2]
和s[i-1]
(即前两个字符)组成的两位数在 10 到 26 之间(包含10和26),那么这两个字符也可以一起解码为一个字母,此时dp[i]
还需要加上dp[i-2]
(即,在dp[i-2]
的基础上,加上这两个字符一起解码的方式)。注意:
- 如果
s[i-1]
是 '0',它不能单独解码,只能与前一个字符一起解码(如果可能的话)。- 如果
s[i-2]
是 '0',那么它不可能与s[i-1]
组成有效的两位数解码,因此不需要考虑这种情况。- 如果
s
的前两个字符组成的两位数不在 10 到 26 之间,那么dp[2]
只能根据s[1]
是否为 '0' 来决定是否为 1 或 0,且dp[1]
的值仅由s[0]
决定。
三、代码实现
public class Solution {
public int numDecodings(String s) {
if (s == null || s.length() == 0 || s.charAt(0) == '0') {
return 0;
}
int n = s.length();
int[] dp = new int[n + 1];
dp[0] = 1; // 空字符串有一种解码方式
dp[1] = 1; // 第一个字符不为 '0' 时,至少有一种解码方式
for (int i = 2; i <= n; i++) {
// 当前字符可以单独解码
if (s.charAt(i - 1) != '0') {
dp[i] += dp[i - 1];
}
// 前两个字符可以组合解码
int twoDigit = (s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0');
if (twoDigit >= 10 && twoDigit <= 26) {
dp[i] += dp[i - 2];
}
}
return dp[n];
}
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println(solution.numDecodings("11106")); // 应输出 2
System.out.println(solution.numDecodings("226")); // 应输出 3
System.out.println(solution.numDecodings("0")); // 应输出 0
}
}
执行结果:
四、小结
这段代码首先检查了几种特殊情况,然后使用了动态规划的思想来解决问题。通过迭代地构建
dp
数组,我们得到了最终的结果。
结语
人生最重要的不是所站的位置
而是所朝的方向
!!!