把数字翻译成字符串| 豆包MarsCode AI刷题

37 阅读5分钟

题目描述

小M获得了一个任务,需要将一个整数转换为对应的字符串。转换规则如下:

  • 数字0对应字符'a',数字1对应字符'b',依此类推,直到数字25对应字符'z'
  • 给定一个非负整数,可能存在多种不同的翻译方法。小M需要编写一个程序,计算该整数有多少种不同的翻译方法。

示例:

  • 输入:12258
  • 输出:5
  • 解释:数字12258可以翻译为 "bccfi", "bwfi", "bczi", "mcfi", 和 "mzi",共5种不同的翻译方法。

解题思路

这道题目可以被视为一种动态规划问题,类似于经典的“解码方法”问题。核心思想是将问题分解为子问题,通过记录和复用子问题的解,最终得到整个问题的解。

具体来说,考虑将给定的数字从右往左进行解析,每个位置可以选择单独翻译一个数字,或者与其前一个数字组合起来翻译(前提是组合后的数字在1025之间,符合转换规则)。

步骤分析:

  1. 合法组合集的构建:

    • 首先,需要构建一个合法的两位数集合,即"10""25"。这些组合代表了可以同时翻译两个数字为一个字符的情况。
  2. 字符串转换:

    • 将输入的整数转换为字符串形式,以便于逐位处理。
  3. 动态规划数组的定义:

    • 定义一个动态规划数组dp,其中dp[i]表示从第i位到末尾的翻译方法数。 方法,否则只有一种。
  4. 递推关系的建立:

    • 对于每一个位置i,至少有dp[i+1]种翻译方法(即单独翻译当前数字)。
    • 如果当前位置ii+1位组成的两位数在合法组合集中,则还可以有dp[i+2]种翻译方法。
    • 因此,dp[i] = dp[i+1] + (如果合法组合则 dp[i+2] 否则 0)
  5. 最终答案:

    • 动态规划数组的第一个元素dp[0]即为整个数字的翻译方法总数。

代码实现

以下是基于上述思路的Java实现代码:

ini
 代码解读
复制代码
java
复制代码
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static int solution(int num) {
        // 构建合法的两位数集合,即 "10" 到 "25"
        final Set<String> legal = new HashSet<>();
        for (int i = 10; i < 26; i++) {
            legal.add(String.valueOf(i));
        }

        String numStr = String.valueOf(num);
        int n = numStr.length();
        
        // 如果数字长度为1,只有一种翻译方法
        if (n <= 1) {
            return 1;
        }

        // dp[i] 表示从第i位到末尾的翻译方法数
        int[] dp = new int[n];
        // 最后一位只有一种翻译方法
        dp[n - 1] = 1;
        // 倒数第二位的翻译方法数取决于是否可以与后一位组成合法的两位数
        dp[n - 2] = legal.contains(numStr.substring(n - 2)) ? 2 : 1;

        // 从倒数第三位开始,逆序填充dp数组
        for (int i = n - 3; i >= 0; i--) {
            // 单独翻译当前位的情况
            int possibilities = dp[i + 1];
            // 检查当前位和下一位组成的两位数是否在合法集合中
            String pred = numStr.substring(i, i + 2);
            if (legal.contains(pred)) {
                possibilities += dp[i + 2];
            }
            dp[i] = possibilities;
        }
        // dp[0] 即为整个数字的翻译方法总数
        return dp[0];
    }

    public static void main(String[] args) {
        // 测试用例
        System.out.println(solution(12258) == 5);      // 输出: true
        System.out.println(solution(1400112) == 6);    // 输出: true
        System.out.println(solution(2110101) == 10);   // 输出: true
    }
}

代码说明:

  1. 合法组合集的构建:

    • 使用HashSet存储所有合法的两位数字字符串,从"10""25"
  2. 动态规划数组的初始化:

    • 将输入数字转换为字符串形式,便于逐位处理。
    • 如果数字长度为1,直接返回1。
    • 初始化动态规划数组dp,其中dp[n-1]设为1,表示最后一位的翻译方法数。
    • 根据倒数第二位和最后一位组成的两位数是否在合法集合中,决定dp[n-2]的值是1还是2。
  3. 动态规划的填充:

    • 从倒数第三位开始,逆序遍历每一位。
    • 对于每一位,首先将dp[i+1]赋值给possibilities,表示单独翻译当前位的情况。
    • 然后,检查当前位和下一位组成的两位数是否在合法集合中,如果是,则加上dp[i+2],表示同时翻译当前位和下一位的情况。
    • 最终,将计算得到的possibilities赋值给dp[i]
  4. 结果输出:

    • 最终,dp[0]即为整个数字的翻译方法总数。

时间复杂度和空间复杂度

时间复杂度:
算法的时间复杂度为O(n),其中n是输入数字的位数。具体分析如下:

  • 构建合法组合集的时间复杂度为O(1),因为循环次数固定为16次(从10到25)。
  • 将数字转换为字符串的时间复杂度为O(n)
  • 动态规划数组的填充需要逆序遍历字符串,每一步的操作为常数时间,因此总体为O(n)

空间复杂度:
算法的空间复杂度为O(n),主要来源于:

  • 动态规划数组dp的使用,占用O(n)的空间。
  • 合法组合集legal占用O(1)的空间,因为其大小固定(16个元素)。
  • 其他辅助变量如字符串表示的数字占用O(n)的空间。

总体而言,算法在时间和空间上的表现都是线性的,能够高效处理较长的输入数字。

总结

通过动态规划的方法,我们可以高效地计算出给定数字的所有可能翻译方法数。关键在于识别和利用子问题的最优子结构性质,通过记录和复用子问题的解,避免了重复计算,从而大幅提升算法的效率。这种方法在处理类似的字符串解析和组合问题中具有广泛的应用价值。

作者:用户36188282613
链接:juejin.cn/post/743976…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。