青训营X豆包MarsCode技术训练营 | 豆包MarsCode AI刷题03

90 阅读4分钟

问题描述

小M获得了一个任务,需要将数字翻译成字符串。翻译规则是:0对应"a",1对应"b",依此类推直到25对应"z"。一个数字可能有多种翻译方法。小M需要一个程序来计算一个数字有多少种不同的翻译方法。

例如:数字12258可以翻译成 "bccfi", "bwfi", "bczi", "mcfi" 和 "mzi",共5种方式。

屏幕截图 2024-11-24 232513.png

解题思路

这道题的核心在于将一个数字字符串翻译成字母字符串,并且计算有多少种不同的翻译方法。翻译规则是0对应"a",1对应"b",依此类推直到25对应"z"。一个数字可能有多种翻译方法,因为每个数字可以单独翻译,也可以与下一个数字组合成一个两位数进行翻译。

动态规划解法

我们可以使用动态规划(Dynamic Programming, DP)来解决这个问题。动态规划的核心思想是将问题分解为子问题,并且利用子问题的解来构建原问题的解。

  1. 定义状态

    • dp[i] 表示前 i 个数字的翻译方法数。
  2. 状态转移方程

    • 如果第 i 个数字可以单独翻译成一个字母(即 num[i-1] 不为0),则 dp[i] 可以由 dp[i-1] 转移而来。
    • 如果第 i-1 和第 i 个数字可以组合成一个有效的两位数(即 10 <= num[i-2:i] <= 25),则 dp[i] 还可以由 dp[i-2] 转移而来。
  3. 初始条件

    • dp[0] = 1,表示空字符串有一种翻译方法(即什么都不翻译)。
    • dp[1] = 1,表示单个数字的翻译方法数。
  4. 最终结果

    • dp[n] 即为所求,其中 n 是数字字符串的长度。

代码实现

def num_decodings(num):
    if not num or num[0] == '0':
        return 0
    
    n = len(num)
    dp = [0] * (n + 1)
    dp[0] = 1
    dp[1] = 1
    
    for i in range(2, n + 1):
        if num[i-1] != '0':
            dp[i] += dp[i-1]
        if '10' <= num[i-2:i] <= '25':
            dp[i] += dp[i-2]
    
    return dp[n]

# 测试样例
print(num_decodings("12258"))  # 输出:5
print(num_decodings("1400112"))  # 输出:6
print(num_decodings("2110101"))  # 输出:10
print(num_decodings("25"))  # 输出:2
print(num_decodings("1023"))  # 输出:4
public class Main {
    public static int solution(int num) {
        // Please write your code here
        String numStr = String.valueOf(num);
        int n = numStr.length();
        int[] dp = new int[n + 1];
        dp[0] = 1; // 空字符串有一种翻译方法

        for (int i = 1; i <= n; i++) {
            // 单独翻译第 i 位数字
            dp[i] = dp[i - 1];

            // 如果第 i-1 位和第 i 位数字组合起来在 10 到 25 之间,则可以组合翻译
            if (i > 1) {
                int twoDigits = Integer.parseInt(numStr.substring(i - 2, i));
                if (twoDigits >= 10 && twoDigits <= 25) {
                    dp[i] += dp[i - 2];
                }
            }
        }

        return dp[n];
    }

    public static void main(String[] args) {
        // You can add more test cases here
        System.out.println(solution(12258) == 5);
        System.out.println(solution(1400112) == 6);
        System.out.println(solution(2110101) == 10);
    }
}

知识点解析

  1. 动态规划(Dynamic Programming, DP)

    • 动态规划是一种通过将问题分解为子问题并存储子问题的解来解决复杂问题的方法。它通常用于解决具有重叠子问题和最优子结构性质的问题。
    • 在这道题中,我们使用动态规划来计算不同翻译方法的数量,通过定义状态和状态转移方程,逐步构建最终的解。
  2. 字符串处理

    • 题目涉及对数字字符串的处理,包括提取子字符串和判断字符串的有效性。
    • 例如,判断两个相邻数字是否可以组合成一个有效的两位数(即 10 <= num[i-2:i] <= 25)。
  3. 边界条件处理

    • 在动态规划中,初始条件的设置非常重要。在这道题中,我们设置了 dp[0] = 1dp[1] = 1 作为初始条件。
    • 还需要处理特殊情况,如数字字符串以 '0' 开头的情况,这种情况下无法进行有效的翻译。
  4. 状态转移方程的理解

    • 状态转移方程是动态规划的核心,理解如何从子问题的解推导出原问题的解是解决这类问题的关键。
    • 在这道题中,状态转移方程考虑了两种情况:单独翻译当前数字和组合翻译当前数字与前一个数字。

通过以上分析和代码实现,我们可以有效地解决这个问题,并且理解动态规划在解决这类问题中的应用。