题目
有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。
现在给一串数字,返回有多少种可能的译码结果
数据范围:字符串长度满足 0<n≤900<n≤90
进阶:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
解析
这是一个经典的 动态规划(Dynamic Programming) 问题,通常被称为 "Decode Ways" 问题。
🔍 问题描述
给定一个仅包含数字的字符串,返回它可能解码成多少种字母组合。
编码规则:
'a'
→1
'b'
→2
- ...
'z'
→26
注意:0 不能单独作为一个字符,也不能作为开头。例如
"0"
、"06"
都是非法的编码。
✅ 动态规划思路
定义 dp[i]
表示前 i
个字符的解码方式总数:
状态转移方程:
-
如果
s[i - 1] != '0'
,说明最后一个字符可以单独译码,所以:javadp[i] += dp[i - 1]
-
如果前两个字符组成的数字在
10 ~ 26
范围内,则这两个字符可以一起译码:javaif (prevTwoDigits >= 10 && prevTwoDigits <= 26) dp[i] += dp[i - 2]
初始条件:
dp[0] = 1
:空字符串有一种解法(即什么都不译)dp[1] = 1
如果第一个字符不是'0'
,否则为0
✅ Java 实现
java
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;
for (int i = 2; i <= n; i++) {
// 单个字符
if (s.charAt(i - 1) != '0') {
dp[i] += dp[i - 1];
}
// 两个字符
int twoDigit = Integer.parseInt(s.substring(i - 2, i));
if (twoDigit >= 10 && twoDigit <= 26) {
dp[i] += dp[i - 2];
}
}
return dp[n];
}
}
✅ 时间与空间复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(n)(可优化至 O(1),只使用两个变量记录前两个状态)
✅ 空间优化版(滚动数组)
java
public class Solution {
public int numDecodings(String s) {
if (s == null || s.length() == 0 || s.charAt(0) == '0') return 0;
int prev2 = 1; // dp[i-2]
int prev1 = 1; // dp[i-1]
for (int i = 2; i <= s.length(); i++) {
int curr = 0;
if (s.charAt(i - 1) != '0') {
curr += prev1;
}
int twoDigit = Integer.parseInt(s.substring(i - 2, i));
if (twoDigit >= 10 && twoDigit <= 26) {
curr += prev2;
}
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
}
✅ 示例
输入:
java
"12"
输出:
2
解释:它可以被译码为 "AB"
(1+2) 或 "L"
(12)
🧠 小贴士
- 处理边界情况要特别小心,比如以
'0'
开头的字符串。 - 可以用
substring
提取子串,也可以用字符运算来提高效率。
如果你还想了解如何打印所有可能的译码结果,或者支持非 ASCII 字符等变体场景,也可以告诉我,我可以继续扩展。