数字翻译成字符串的可能性| python解法
题目分析
本题是一个经典的动态规划问题,主要考察字符串处理与状态转移。题目要求将给定的数字翻译为字符串,翻译规则是:0 对应 "a",1 对应 "b",以此类推,25 对应 "z"。给定一个数字,求有多少种不同的翻译方式。例如,数字 12258 可以被翻译为 "bccfi", "bwfi", "bczi", "mcfi" 和 "mzi",共计 5 种不同的翻译方法。
解题思路
我们可以发现,问题本质上是如何将数字字符串分割成一个个子串,使得这些子串可以翻译成字母。要解决这个问题,可以有以下几种方法:
- 递归法:将字符串按字符逐一拆分或者按两字符拆分,利用递归方式探索所有可能的翻译方案。每次递归时,判断当前拆分是否符合翻译规则(即
0到25之间),如果符合则继续拆分。这种方法简单但效率较低,容易导致重复计算和时间超时。 - 动态规划法:利用动态规划解决重复子问题。动态规划的核心思想是将问题分解为更小的子问题,逐步解决,并记录每一步的结果以便后续使用,避免重复计算。这个方法效率较高,是解本题的最佳方案。
基本思想
动态规划解决问题的基本步骤如下:
-
状态定义:
- 定义
dp[i]为数字字符串的前i个字符的翻译方法总数。
- 定义
-
状态转移方程:
- 如果第
i-1个字符可以独立翻译(即不为'0'),则dp[i] += dp[i-1]。 - 如果第
i-2和i-1组成的两位数在10到25之间,则dp[i] += dp[i-2],这意味着这两位数可以作为一个整体翻译成一个字母。
- 如果第
-
初始化:
dp[0] = 1,表示空字符串有一种翻译方法。dp[1] = 1,表示单个字符有一种翻译方法。
-
结果:
- 最终结果为
dp[n],其中n是字符串的长度。
- 最终结果为
关键知识点总结
-
动态规划:
- 动态规划适用于可以被分解为重复子问题的问题。它通过记录中间结果,避免重复计算,提高效率。
-
字符串处理:
- 要判断两个字符组成的子串是否可以翻译,需要考虑其是否在
"10"到"25"之间。
- 要判断两个字符组成的子串是否可以翻译,需要考虑其是否在
-
边界条件:
- 数字中可能包含
0,在这种情况下需要特别处理,避免翻译错误。例如"10"可以翻译为"j",但"01"不可翻译。
- 数字中可能包含
复杂度分析
- 时间复杂度:
O(n),其中n是数字的长度。每个字符只需处理一次,没有重复计算。 - 空间复杂度:
O(n),需要一个长度为n+1的数组存储动态规划结果。可以通过滚动数组优化为O(1)。
代码实现
def solution(num):
# 将输入数字转换为字符串,方便处理每一位数字
str_num = str(num)
n = len(str_num)
# 边界情况处理:如果字符串为空,返回 0;如果长度为 1,只有一种翻译方式
if n == 0:
return 0
if n == 1:
return 1
# 初始化 dp 数组
dp = [1, 1] # dp[0] = 1, dp[1] = 1 为基准,前两个字符的翻译方式
# 从第 2 个字符开始,逐步计算每个位置的翻译方法数
for i in range(2, n + 1):
dp_i = dp[i - 1] # 默认翻译当前字符
# 检查当前字符和前一个字符是否可以组成一个有效的两位数翻译
two_digit = int(str_num[i - 2:i]) # 提取当前和前一个字符形成的两位数
if 10 <= two_digit <= 25:
dp_i += dp[i - 2] # 如果两位数可以翻译,加入前两位的翻译方法数
dp.append(dp_i) # 将 dp[i] 加入数组
# 返回 dp[n],即整个字符串的翻译方法总数
return dp[-1] # 最终的翻译方法总数
扩展适用场景
这道题的解决方法可以推广到以下场景:
-
字母解码问题:
- 类似于 "Leetcode 91. 解码方法"(Decode Ways),其中每个数字代表一个字母(如
1对应"A",26对应"Z")。这道题也是通过动态规划求解,思路相似。
- 类似于 "Leetcode 91. 解码方法"(Decode Ways),其中每个数字代表一个字母(如
-
分割子字符串问题:
- 如果需要将字符串按特定规则分割为多个部分并统计分割方式,可以借鉴这种动态规划的思路。
-
拼音数字翻译:
- 类似地,如果题目要求将数字翻译为其他语言的字符或拼音,并且有一定的映射关系,也可以使用动态规划进行求解。
总结
本题的解题关键在于:
- 使用动态规划避免重复计算,提高效率。
- 合理处理数字的边界情况,尤其是包含
0的情况。 - 定义好状态转移方程,利用前面的计算结果递推得到最终解。