掘金刷题之路 Day 6 | 豆包MarsCode AI刷题

62 阅读3分钟

数字翻译成字符串的可能性

借用AI工具分析题目

从给出的分析结果发现这题目可以使用动态规划进行解答

动态规划思想解析

动态规划的核心是通过子问题的解来构造更大问题的解。在这个问题中:

  1. 状态定义
    • 定义 dp[i] 为数字字符串前 i 位的翻译方法总数。
    • 通过递推关系,可以逐步构建 dp[i] 的值。
  1. 状态转移方程
    • 如果只考虑当前位置的单个数字翻译,那么 dp[i] = dp[i-1]
    • 如果考虑当前位置和前一个位置的数字可以组成一个有效翻译(值在 [10, 25] 范围内),那么还可以将 dp[i-2] 的贡献加上,即:
    • 总体关系为:
  1. 初始条件
    • dp[0] = 1:空字符串的翻译方法只有一种(没有字符)。
    • dp[1] = 1:一个字符的翻译方法只有一种。

解释

String str = String.valueOf(num);
int n = str.length();
int[] dp = new int[n + 1];
  • 将数字 num 转换为字符串 str,便于操作数字的每一位。
  • dp 数组的长度为 n+1,其中 dp[i] 表示前 i 个字符的翻译方法数。

dp[0] = 1;
dp[1] = 1;
  • 初始化状态:空字符串和单字符字符串的翻译方法。

for (int i = 2; i <= n; i++) {
    dp[i] = dp[i - 1]; // 默认情况下可以单独翻译第i个字符
    int twoDigit = Integer.parseInt(str.substring(i - 2, i)); // 取出最后两个字符
    if (twoDigit >= 10 && twoDigit <= 25) {
        dp[i] += dp[i - 2]; // 如果两位字符合法,加上它们的翻译方法
    }
}
  • 遍历字符串的每一位,从第2位开始,逐步计算 dp[i]
  • dp[i-1] 表示当前字符单独翻译的贡献。
  • 检查 twoDigit(由当前字符和前一个字符组成的两位数)是否在 [10, 25] 范围内。如果是,则加上 dp[i-2] 的贡献,因为这两位数字可以作为一个整体翻译。

return dp[n];
  • 最终的翻译方法数为 dp[n],即整个字符串的翻译方法数。

示例分析

12258 为例:

  • 数字对应的字符串为 12258,其长度为 n=5
  • 初始化 dp 数组:dp[0]=1dp[1]=1
  • 从第2位开始迭代:
    • i=2dp[2] = dp[1] + dp[0] (因为 12[10, 25] 范围内)。
      • dp[2] = 1 + 1 = 2
    • i=3dp[3] = dp[2] (因为 22[10, 25] 范围内)。
      • dp[3] = dp[2] + dp[1] = 2 + 1 = 3
    • i=4dp[4] = dp[3] + dp[2] (因为 25[10, 25] 范围内)。
      • dp[4] = 3 + 2 = 5
    • i=5dp[5] = dp[4] (因为 58 不在 [10, 25] 范围内)。
      • dp[5] = dp[4] = 5
  • 最终 dp[5] = 5,对应的翻译方法有:bccfibwfibczimcfimzi

为什么这样写?

  1. 动态规划高效解法
    • 通过定义状态和递推关系,避免重复计算子问题,降低复杂度。
    • 时间复杂度:(O(n)),仅需一次遍历。
    • 空间复杂度:(O(n)),仅需一个数组存储状态。
  1. 逻辑清晰,易于理解
    • 每一步的计算都基于明确的状态转移关系。
    • 初始化简单,递推关系符合问题描述。

优化建议

可以将 dp 数组优化为两个变量,因为当前状态仅依赖于前两个状态:

public static int solution(int num) {
    String str = String.valueOf(num);
    int n = str.length();
    int prev2 = 1, prev1 = 1;
    
    for (int i = 2; i <= n; i++) {
        int current = prev1;
        int twoDigit = Integer.parseInt(str.substring(i - 2, i));
        if (twoDigit >= 10 && twoDigit <= 25) {
            current += prev2;
        }
        prev2 = prev1;
        prev1 = current;
    }
    
    return prev1;
}
  • 时间复杂度仍为 (O(n)),但空间复杂度降为 (O(1))。

唉还是感觉好难,还得继续练习dp类的题目