LeetCode 剑指 Offer 46. 把数字翻译成字符串
题目描述

题意分析
将非负整数的十进制数字从左到右分组,每组按
0..25映射为一个字母,求不同翻译的数量。每组只能占一位或两位:单个数字都合法,两位组成的数必须在10..25内,不能带前导零。
解法:滚动动态规划
核心思路
[!blue]
令
dp[i]表示前i位数字的翻译数量。观察最后一个字母用了几位,就能把所有方案分成互不重叠的两类:最后一位单独翻译,前面的i - 1位有dp[i - 1]种方案;最后两位整体翻译,则要求它们组成的数在10..25内,前面的i - 2位有dp[i - 2]种方案。因而两位合法时,
dp[i] = dp[i - 1] + dp[i - 2];否则只有单独翻译末位这一种接法,dp[i] = dp[i - 1]。最后一组不可能超过两位,这两类已经覆盖全部方案;它们的分组位置不同,不会重复计数。初值
dp[0] = 1表示空前缀只有一种分组方式,保证开头两位整体翻译时能贡献一种方案;dp[1] = 1,因为单个数字包括0都可翻译。输入只有一位时无需转移,答案就是 1。每次只依赖前两个状态,用
pre和cur滚动保存。处理下标idx前,它们分别等于dp[idx - 1]和dp[idx];先求出next = dp[idx + 1],再同时向前推进,避免覆盖仍需使用的旧值。
解题步骤
- 将数字转为十进制字符串,初始化
pre = 1、cur = 1,分别对应dp[0]、dp[1]。- 从第二位开始,计算当前相邻两位组成的数
twoDigits。- 若
10 <= twoDigits <= 25,令next = pre + cur;否则next = cur。- 用旧
cur更新pre,再令cur = next,最终返回cur。
代码实现
class Solution {
public int translateNum(int num) {
String text = String.valueOf(num);
int pre = 1;
int cur = 1;
for (int idx = 1; idx < text.length(); idx++) {
int twoDigits = (text.charAt(idx - 1) - '0') * 10 + text.charAt(idx) - '0';
int next = cur;
// 只有 10 到 25 可以作为两位整体翻译,06 这类不能合并。
if (twoDigits >= 10 && twoDigits <= 25) {
next = pre + cur;
}
pre = cur;
cur = next;
}
return cur;
}
}
import "strconv"
func translateNum(num int) int {
text := strconv.Itoa(num)
pre, cur := 1, 1
for idx := 1; idx < len(text); idx++ {
twoDigits := int(text[idx-1]-'0')*10 + int(text[idx]-'0')
next := cur
// 只有 10 到 25 可以作为两位整体翻译,06 这类不能合并。
if twoDigits >= 10 && twoDigits <= 25 {
next = pre + cur
}
pre = cur
cur = next
}
return cur
}
复杂度分析
- 时间复杂度:$O(n)$,
n为十进制位数。- 空间复杂度:$O(n)$,用于十进制字符串;DP 状态为 $O(1)$。
关键点总结
[!green]
dp[i]按前缀长度定义,转移只需判断末尾两位是否可以合并。- 单独翻译末位与合并末尾两位的方案互斥,所以两位合法时相加;不合法时仍保留单独翻译末位的方案。
dp[0] = 1是计数起点,num = 0也有一种翻译,不能返回 0。- 与 91 题不同:本题映射范围为
0..25,而 91 题中单个0非法、两位上界为 26。
易错点总结
[!yellow]
- 两位组合必须同时满足下界 10 和上界 25,否则会错误接受前导零或超出映射范围的组合。
- 单个
0合法,不能套用“遇到零就无解”的判断。- 滚动更新顺序写反会覆盖上一轮状态,必须先计算
next。- 字符转数字时不要漏掉减
'0'。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 91. 解码方法 | 中等 | 原题映射1到26且0不能单独解码,本题映射0到25,单个0合法但双位26不合法。 |
| 70. 爬楼梯 | 简单 | 都可按消费1位或2位计数递推,但本题两位合并只在10到25之间合法。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!