数字翻译成字符串的可能性:解题思路与代码详解 | 豆包MarsCode AI刷题

172 阅读5分钟

题目解析

题目要求我们计算一个数字(如 12258)的所有可能翻译方式,其中翻译规则如下:

  • 数字 0 对应字符 'a',数字 1 对应字符 'b',以此类推,直到数字 25 对应字符 'z'
  • 数字串可以通过不同的方式进行翻译,一些数字可能被单独翻译,也可以和相邻的数字组合翻译。

例如,对于数字 12258,它可以通过以下几种方式进行翻译:

  • "bccfi"
  • "bwfi"
  • "bczi"
  • "mcfi"
  • "mzi"

因此,总共有 5 种翻译方式。

思路分析

我们可以使用动态规划来解决这个问题。定义 dp[i] 为前 i 个数字的翻译方式数量。那么,dp[i] 的值可以通过以下两种情况来递推:

  1. 当前数字单独翻译:我们可以将当前数字(s[i-1])单独翻译为一个字符,因此 dp[i] 至少等于 dp[i-1]

  2. 当前数字与前一个数字组合翻译:如果当前数字和前一个数字可以组合成一个合法的翻译(即它们的值在 10 到 25 之间),则我们也可以把这两个数字当作一个整体进行翻译,这时 dp[i] 还需要加上 dp[i-2]

动态规划递推式:

  • dp[i] = dp[i-1] + dp[i-2](如果 s[i-2:i] 的值在 10 到 25 之间)

初始条件:

  • dp[0] = 1:空字符串只有 1 种翻译方式(就是什么也不翻译)。
  • dp[1] = 1:单个字符有 1 种翻译方式。

代码实现

def solution(num):
    # 转换为字符串,方便处理每一位
    s = str(num)
    n = len(s)
    
    # dp[i]表示前i个数字的翻译方法数
    dp = [0] * (n + 1)
    dp[0] = 1  # 空字符串有1种翻译方法
    dp[1] = 1  # 第一个数字只有1种翻译方法
    
    for i in range(2, n + 1):
        # 当前数字可以单独翻译
        dp[i] = dp[i-1]
        
        # 检查当前数字能否与前一个数字组合翻译
        two_digits = int(s[i-2:i])
        # 组合的数字必须在10-25之间才能翻译
        if 10 <= two_digits <= 25:
            dp[i] = dp[i-2] + dp[i-2]
    
    return dp[n]

if __name__ == "__main__":
    # 测试用例
    print(solution(12258))    # 输出 5
    print(solution(1400112))  # 输出 6
    print(solution(2110101))  # 输出 10
    print(solution(25))       # 输出 2
    print(solution(1023))     # 输出 4

代码详解

  1. 转换为字符串

    • 为了方便处理每一位数字,我们首先将输入的数字转换成字符串。
  2. 初始化 dp 数组

    • dp[i] 表示前 i 个数字的翻译方法数。初始化时,dp[0] = 1dp[1] = 1
  3. 动态规划递推

    • i=2 开始,逐个计算每个位置的翻译方式数。
    • 如果当前位置的数字可以单独翻译,就继承前一个位置的翻译方式数。
    • 如果当前位置的数字和前一个数字组合成一个合法的翻译(即 10 <= two_digits <= 25),就加上前两个位置的翻译方式数。
  4. 返回结果

    • 最终的翻译方法数保存在 dp[n] 中,其中 n 是输入数字的长度。

时间复杂度:

动态规划的时间复杂度是 O(n),其中 n 是数字的长度,因为我们只需要遍历一次数字。

空间复杂度:

空间复杂度是 O(n),我们需要一个长度为 n+1 的数组来保存中间结果。

示例解析

如果理解的不是很清晰,可以代入一个示例看一看:

示例 1:num = 12258

  • 初始 dp = [1, 1, 0, 0, 0, 0]
  • 计算过程:
    • dp[2] = dp[1] = 1,因为数字 1 可以翻译为 'b'
    • dp[3] = dp[2] = 1,数字 2 可以翻译为 'c'
    • dp[4] = dp[3] + dp[2] = 2,数字 5 可以翻译为 'f',同时 25 可以翻译为 'z'
    • dp[5] = dp[4] + dp[3] = 3,数字 8 可以翻译为 'i'
  • 结果:dp[5] = 5

优化思路

  1. 空间优化的关键:原来的 dp 数组中,每个 dp[i] 只与 dp[i-1]dp[i-2] 相关。因此,我们只需要记录 dp[i-1]dp[i-2],而不是保存整个数组。

  2. 定义两个变量

    • prev2:表示 dp[i-2]
    • prev1:表示 dp[i-1]
  3. 计算过程:通过循环更新 prev1prev2 来得到当前 dp[i],最终得到 dp[n]

空间优化版本的代码实现

def solution(num):
    # 转换为字符串,方便处理每一位
    s = str(num)
    n = len(s)
    
    # 边界条件:当数字为空时或只有一位时
    if n == 0:
        return 0
    if n == 1:
        return 1
    
    # prev2 和 prev1 分别表示 dp[i-2] 和 dp[i-1]
    prev2, prev1 = 1, 1  # 初始化 dp[0] = 1, dp[1] = 1
    
    for i in range(2, n + 1):
        # 当前数字可以单独翻译
        current = prev1
        
        # 检查当前数字能否与前一个数字组合翻译
        two_digits = int(s[i-2:i])
        if 10 <= two_digits <= 25:
            current += prev2
        
        # 更新 prev2 和 prev1
        prev2 = prev1
        prev1 = current
    
    return prev1

# 测试用例
print(solution(12258))    # 输出 5
print(solution(1400112))  # 输出 6
print(solution(2110101))  # 输出 10
print(solution(25))       # 输出 2
print(solution(1023))     # 输出 4

空间复杂度分析

  • 这个优化版本的空间复杂度是 O(1),因为我们只使用了常数级别的空间(prev2prev1),不再需要一个完整的数组来存储所有的 dp 状态。

时间复杂度分析

  • 这个版本的时间复杂度依然是 O(n),因为我们仍然需要遍历整个数字字符串,只是我们不再需要额外的空间来存储所有的状态。