第89篇 LeetCode剑指Offer动态规划(六)把数字翻译成字符串

本文详细介绍了如何使用动态规划解决LeetCode剑指Offer中的一道题目,即计算一个数字有多少种不同的翻译成字符串的方法。通过递归和迭代的方式,逐步解析解题思路,包括dp数组的定义、递推式、初始化、递归函数及迭代优化。最终给出优化后的迭代解法,有效避免超时问题。
摘要由CSDN通过智能技术生成

1.题目描述

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

示例 1:

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

提示:

0 <= num < 231

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.动态规划的解题步骤

2.1.dp[i]的定义

对上题而言,我们举一个数字,如12258,那么dp[12258]就表示12258这个数字有几种翻译方式
dp[i]就表示i这个数字有几种翻译方式。

2.2.递推式

如12258:自顶向下思想,因为字符对应的数字有一位数和两位数,所以如果我们先把8翻译成一个字符,那么12258的翻译方式就是1225的翻译方式,而如果取58(假定可以翻译),那么12258的翻译方式就是122的翻译方式,那么12258总的翻译方式就是1225的翻译方式和122的翻译方式的和。因此我们可以初步断定:
dp[i] = dp[i / 10] + dp[i / 100];
看一个数字:102
按照上面的方法dp[102] = dp[10] + dp[1] = 3;
但是102只有两种:bac,ic
因此如果num % 100 < 10 或者num % 100 > 25的时候,num / 100是不能够去单独计算的,这样就会造成重复计算了里面的个位数。比如102重复计算了2:,所以改成

dp[i] = dp[i / 10] +(i % 100 > 9 && i % 100 < 26) ? dp[i / 100] : 0;

2.3.dp初始化和递归函数出口

如果数字只有一位,直接返回1,只有一种翻译方法。
if(num < 10) {
return 1;
}

2.4.递归函数

非常的简洁漂亮。简洁的代码干的事非常多。

int opt_recursion(int num) {
    if(num < 10) {
        return 1;
    }

    int count1 = opt_recursion(num / 10);
    int count2 = 0;
    if(num % 100 > 9 && num % 100 < 26) {
        count2 = opt_recursion(num / 100);
    }

    return count1 + count2;
}

)

这道题的递归非常快,反而迭代慢了。

2.5.递归改成迭代

每个数字至少有一种翻译方式

int opt_iterate(int num) {
    vector<int> dp(num, 1);

    int n = 1;
    while(n <= num) {
        int count1 = dp[n / 10];
        int count2 = 0;
        if(n % 100 > 9 && n % 100 < 26) {
            count2 = dp[n / 100];
        }
        dp[n - 1] = count1 + count2;
        n++;
    }

    return dp[num - 1];
}

在这里插入图片描述
超时了,因为数字太大,O(n)就大了,超时,比如12258就执行12258次。

2.6.迭代优化

用哈希表记录num中数字的情况,每一种数字组成的情况。
这样时间复杂度就是O(num.size()),12258就执行5次就行了。

int opt_iterate(int num) {
    unordered_map<int,int> dp;
    dp[0] = 1;
    deque<int> nums;
    int n = num;
    while(n > 0) {
        nums.push_front(n % 10);
        dp[n % 10] = 1;
        n /= 10;
    }

    int i = 0;
    int len = nums.size();
    int currnum = 0;
    for(int i = 0;i < len;i++) {
        currnum = currnum * 10 + nums[i];
        int count1 = dp[currnum / 10];
        int count2 = 0;
        if(currnum % 100 > 9 && currnum % 100 < 26) {
            count2 = dp[currnum / 100];
        }
        dp[currnum] = count1 + count2;
    }


    return dp[num];
}

在这里插入图片描述
继续空间优化:
每次使用的都是num / 10和num / 100的数据来更新num的翻译次数,因此只需记录这两个值就行了;

int opt_iterate_better(int num) {
    deque<int> nums;
    int n = num;
    while(n > 0) {
        nums.push_front(n % 10);
        n /= 10;
    }

    int ans = 1;
    int ans_model_ten = 1;
    int ans_model_hundred = 1;
    int currnum = 0;
    for(int value: nums) {
        currnum = currnum * 10 + value;
        int count1 = ans_model_ten;
        int count2 = 0;
        if(currnum % 100 > 9 && currnum % 100 < 26) {
            count2 = ans_model_hundred;
        }
        ans = count1 + count2;
        ans_model_hundred = ans_model_ten;
        ans_model_ten = ans;
    }

    return ans;
}

3.结语

能做出来后,优化就方便多了,记得自顶向下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大唐不良猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值