问题分析
将一个数字翻译成字符串,类似于“解码”问题,根据翻译规则,每个数字可以对应一个字母,可能存在单独翻译和成对翻译的多种方式。因此问题的本质是动态规划问题,寻找从左到右分割数字的方法数量。
解题思路
- 单个字符翻译:
- 每个数字
0-9都可以单独翻译。 - 例如:
12258中的1可以翻译成'b'。
- 两个字符翻译:
- 每一对连续的数字,如果在
10-25之间,可以一起翻译。 - 例如:
12258中的12可以翻译成'm'。
- 动态规划公式:
- 定义
dp[i]为前i个数字有多少种翻译方法。 - 转移公式: [ dp[i] = dp[i-1] + dp[i-2] \text{ (如果 num[i-2] 和 num[i-1] 组成的数字在 [10, 25])} ]
- 初始状态:
dp[0] = 1:空数字有 1 种翻译方法。dp[1] = 1:第一个数字可以翻译为一个字符。
- 实现步骤:
- 转换数字为字符串,逐位检查是否可以单独翻译或成对翻译。
- 使用数组或两个变量记录
dp的值,节约空间。
代码实现
public class Main {
public static int solution(int num) {
// 将数字转换为字符串方便处理
String numStr = String.valueOf(num);
int n = numStr.length();
// 特殊情况:只有一个数字
if (n == 0) return 0;
if (n == 1) return 1;
// 定义 dp 数组
int[] dp = new int[n + 1];
dp[0] = 1; // 空字符串翻译方法为 1
dp[1] = 1; // 第一个字符翻译方法为 1
// 动态规划过程
for (int i = 2; i <= n; i++) {
// 单独翻译当前字符
dp[i] = dp[i - 1];
// 检查两个字符是否可以一起翻译
int twoDigit = Integer.parseInt(numStr.substring(i - 2, i));
if (twoDigit >= 10 && twoDigit <= 25) {
dp[i] += dp[i - 2];
}
}
// 最终结果存储在 dp[n]
return dp[n];
}
public static void main(String[] args) {
System.out.println(solution(12258) == 5); // 5
System.out.println(solution(1400112) == 6); // 6
System.out.println(solution(2110101) == 10); // 10
System.out.println(solution(25) == 2); // 2
System.out.println(solution(1023) == 4); // 4
}
}
代码详解
- 输入处理:
- 使用
String.valueOf(num)将数字转换为字符串,方便逐位检查。
- 动态规划数组初始化:
dp[0] = 1:空字符串有一种翻译方法。dp[1] = 1:单个字符的数字只能翻译成一个字母。
- 动态转移:
- 单独翻译:当前数字总是可以单独翻译,继承
dp[i-1]。 - 成对翻译:如果连续两位数字满足条件(
10 <= twoDigit <= 25),增加dp[i-2]。
- 返回结果:
- 最终结果为
dp[n],即整个字符串的翻译方法数量。
优化方案
空间优化
由于 dp[i] 只依赖于 dp[i-1] 和 dp[i-2],可以将空间复杂度从 (O(n)) 降为 (O(1))。
public static int solution(int num) {
String numStr = String.valueOf(num);
int n = numStr.length();
if (n == 0) return 0;
if (n == 1) return 1;
int prev2 = 1; // dp[i-2]
int prev1 = 1; // dp[i-1]
int current = 0;
for (int i = 2; i <= n; i++) {
current = prev1;
int twoDigit = Integer.parseInt(numStr.substring(i - 2, i));
if (twoDigit >= 10 && twoDigit <= 25) {
current += prev2;
}
prev2 = prev1;
prev1 = current;
}
return prev1;
}
测试结果
示例 1: num = 12258
- 翻译方式:
'bccfi''bwfi''bczi''mcfi''mzi'
- 输出:5
示例 2: num = 1400112
- 翻译方式:
'nibbba''nibbbk''nbibbba''nbibbbk''bbbba''bbbbk'
- 输出:6
示例 3: num = 2110101
- 翻译方式共有 10 种。
- 输出:10
时间和空间复杂度
- 时间复杂度:(O(n))
- 遍历字符串每一位。
- 空间复杂度:
- 使用动态数组:(O(n))。
- 空间优化后:(O(1))。
总结
- 本题是经典的动态规划问题。
- 使用状态转移公式简化问题,优化后的实现高效且易读。
- 可以进一步扩展为变种,增加其他翻译规则或字符限制。