Leetcode刷题笔记——剑指 Offer 46. 把数字翻译成字符串(中等)

该博客讨论了一种使用动态规划解决数字翻译成字母的算法问题。文章介绍了两种方法,一种是通过字符串遍历,另一种是利用数字求余,分别分析了它们的时间和空间复杂度,并提供了C++代码实现。在动态规划中,通过维护两个状态来减少空间复杂度,最终得出数字的翻译方案数量。
摘要由CSDN通过智能技术生成


题目描述

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”

解法:动态规划

根据题意,可按照下图的思路,总结出 “递推公式” (即转移方程)。
因此,此题可用动态规划解决,以下按照流程解题。
在这里插入图片描述
动态规划解析:

记数字 n u m num num i i i 位数字为 x i x_i xi ,数字 n u m num num 的位数为 n n n
例如: n u m = 12258 num = 12258 num=12258 n = 5 n = 5 n = 5n=5 n=5n=5 , x 1 = 1 x_1 = 1 x1=1

  • 状态定义: 设动态规划列表 dp , d p [ i ] dp[i] dp[i] 代表以 x i x_i xi 为结尾的数字的翻译方案数量。

  • 转移方程: x i x_i xi x i − 1 x_{i-1} xi1 组成的两位数字可以被翻译,则 d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i] = dp[i - 1] + dp[i - 2] dp[i]=dp[i1]+dp[i2] ;否则 d p [ i ] = d p [ i − 1 ] dp[i] = dp[i - 1] dp[i]=dp[i1]

    • 可被翻译的两位数区间:当 x i − 1 = 0 x_{i-1} = 0 xi1=0 时,组成的两位数是无法被翻译的(例如 00 , 01 , 02 , ⋯ 00, 01, 02, \cdots 00,01,02,),因此区间为 [ 10 , 25 ] [10, 25] [10,25]
      在这里插入图片描述
  • 初始状态: d p [ 0 ] = d p [ 1 ] = 1 dp[0] = dp[1] = 1 dp[0]=dp[1]=1 ,即 “无数字” 和 “第 1 1 1 位数字” 的翻译方法数量均为 1 1 1

  • 返回值: d p [ n ] dp[n] dp[n] ,即此数字的翻译方案数量。

Q: 无数字情况 d p [ 0 ] = 1 dp[0]=1 dp[0]=1 从何而来?
A: 当 n u m num num 1 , 2 1, 2 1,2 位的组成的数字 ∈ [ 10 , 25 ] \in [10,25] [10,25] 时,显然应有 2 2 2 种翻译方法,即 d p [ 2 ] = d p [ 1 ] + d p [ 0 ] = 2 dp[2] = dp[1] + dp[0] = 2 dp[2]=dp[1]+dp[0]=2 ,而显然 d p [ 1 ] = 1 dp[1] = 1 dp[1]=1 ,因此推出 d p [ 0 ] = 1 dp[0] = 1 dp[0]=1

方法一:字符串遍历

  • 为方便获取数字的各位 x i x_i xi ,考虑先将数字 n u m num num 转化为字符串 s s s ,通过遍历 s s s 实现动态规划。
  • 通过字符串切片 s [ i − 2 : i ] s[i - 2:i] s[i2:i] 获取数字组合 10 x i − 1 + x i 10 x_{i-1} + x_i 10xi1+xi ,通过对比字符串 ASCII 码判断字符串对应的数字区间。
  • 空间使用优化: 由于 d p [ i ] dp[i] dp[i] 只与 d p [ i − 1 ] dp[i - 1] dp[i1] 有关,因此可使用两个变量 a , b a, b a,b 分别记录 d p [ i ] , d p [ i − 1 ] dp[i], dp[i - 1] dp[i],dp[i1] ,两变量交替前进即可。此方法可省去 d p dp dp 列表使用的 O ( N ) O(N) O(N) 的额外空间。

复杂度分析

  • 时间复杂度 O ( N ) O(N) O(N) N N N 为字符串 s s s 的长度(即数字 n u m num num 的位数 log ⁡ ( n u m ) \log(num) log(num)),其决定了循环次数。
  • 空间复杂度 O ( N ) O(N) O(N) : 字符串 s s s 使用 O ( N ) O(N) O(N) 大小的额外空间。

C++代码实现

class Solution {
public:
    int translateNum(int num) {
        string str = to_string(num);
        int len = str.size();
        if(len < 2) return len;
        vector<int> dp(len+1);
        dp[1] = 1;
        dp[0] = 1;
        for(int i = 2;i <= len;i++){
            if(str[i-2] == '1' || (str[i-2] == '2' && str[i-1] <= '5')) dp[i] = dp[i-2]+dp[i-1];
            else dp[i] = dp[i-1];
        }
        return dp[len];
    }
};

方法二:数字求余

  • 上述方法虽然已经节省了 d p dp dp 列表的空间占用,但字符串 s s s 仍使用了 O ( N ) O(N) O(N) 大小的额外空间。

空间复杂度优化:

  • 利用求余运算 n u m % 10 num \% 10 num%10 和求整运算 n u m / / 10 num // 10 num//10 ,可获取数字 n u m num num 的各位数字(获取顺序为个位、十位、百位…)。
  • 因此,可通过 求余求整 运算实现 从右向左 的遍历计算。而根据上述动态规划 “对称性” ,可知从右向左的计算是正确的。
  • 自此,字符串 s s s 的空间占用也被省去,空间复杂度从 O ( N ) O(N) O(N) 降至 O ( 1 ) O(1) O(1)

复杂度分析

  • 时间复杂度 O ( N ) O(N) O(N) N N N 为字符串 s s s 的长度(即数字 n u m num num 的位数 log ⁡ ( n u m ) \log(num) log(num),其决定了循环次数。
  • 空间复杂度 O ( 1 ) O(1) O(1) : 几个变量使用常数大小的额外空间。

C++代码实现

class Solution {
public:
    int translateNum(int num) {
        int a = 1, b = 1, x, y = num % 10;
        while(num != 0) {
            num /= 10;
            x = num % 10;
            int tmp = 10 * x + y;
            int c = (tmp >= 10 && tmp <= 25) ? a + b : a;
            b = a;
            a = c;
            y = x;
        }
        return a;
    }
};

参考链接

[1] https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/solution/mian-shi-ti-46-ba-shu-zi-fan-yi-cheng-zi-fu-chua-6/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卑微小岳在线debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值