题解 | #把数字翻译成字符串# 动态规划

把数字翻译成字符串

http://www.nowcoder.com/practice/046a55e6cd274cffb88fc32dba695668

动态规划

①分析题目:能够译码的数字不会大于26,即有效的译码范围为 [1,26]。

②确定状态:以数组 "12345" 为例,首先要明确两点:

  • 依题意可知,数组中所有的数字都必须参与译码,比如数组 "1234" 的某一种译码方式为 “1,2,3,4",那么当数组尾再添加一个元素 "5" 时,刚才的译码方式就会变为 "1,2,3,4,5",即 "1,2,3,4" 和 "1,2,3,4,5" 是同一种译码方式。

    由于所有数字都要参与译码,所以只要译码方式中存在个位数 "0",那么就都是无效的。

  • 当新加入一个数字"5" 时,其除了可以单独作为一个数字参与译码外,也可以与其左边的数字组成数字组合后再参与译码,即 "45" ,然后此时有多少种译码方式就取决于剩余的数字 即 "123" 了,这和前面所说的是同一个道理,只是此时把 "4"和"5" 看作是一个整体,可以理解为:原本数组 "123" 存在某种译码方式为 "1,2,3",现在加入 "45",译码方式就变成了 "1,2,3,45"。

    由于有效的译码范围为 [1,26],所以新加入的数字 "5" 只需要考虑与其左边的数字组成两位数组合,无需再考虑组成更大的组合了,比如三位数 "345" 等等。

    数字组合必须是十位数,比如 "0"和"2" 组成的 "02" 也是不符合译码要求的。

理解了上面两点后,现在我们从数组 "12345" 的子串"1"开始分析,然后逐步往后添加元素,设数组 x 有 f(x) 种有效的译码方式:

  • 当数组为 "1" 时,有以下译码方式:

    ①1

    f("1")=1

  • 接着加入数字"2",数组为 "12" 时,有以下译码方式:

    ①1,2

    ②12

    f("12")=2

    其中第①种是从 数组 "1" 的译码方式 演变过来的,就是在其基础上再加上单个数字 "2"。

  • 接着加入数字"3",数组为 "123" 时,有以下译码方式:

    ①1,2,3

    ②12,3

    ③1,23

    f("123")=3

    其中第①、②种是从 数组 "12" 的译码方式 演变过来的,就是在其基础上再加上单个数字 "3";

    而第③种是从 数组 "1" 的译码方式 演变过来的,就是在其基础上再加上数字组合 "23"。

  • 接着加入数字"4",数组为 "1234" 时,有以下译码方式:

    ①1,2,3,4

    ②12,3,4

    ③1,23,4

    ④1,2,34

    ⑤12,34

    其中第①、②、③种是从 数组 "123" 的译码方式 演变过来的,就是在其基础上再加上单个数字 "4";

    而第④、⑤种是从 数组 "12" 的译码方式 演变过来的,就是在其基础上再加上数字组合 "34";

    由于 "45" 不在有效译码的范围内,所以这两种译码方式会被抛弃掉。

    f("1234")=3

    可见,数组 "1234" 的译码方式 是由 数组 "123"的译码方式数组 "12"的译码方式 组成的。

  • 根据上面的分析,当数组继续扩张到 "12345" 时,那么其译码方式就为:

    当新加入的数字 "5" 单独作为个位数参与译码时,此时的译码方式 就等同于 数组 "1234" 的译码方式,这组译码方式是有效的,因为个位数 "5" 在有效的译码范围内;

    而 "5" 与其前一位数字组成十位数 "45" 时,此时的译码方式 就等同于 数组 "123" 的译码方式,而这组译码是否有效则取决于 "45" 是否在有效的译码范围内。

    即 f("12345") = f("12345" - "5") + f("12345" - "45") = f("1234") + f("123") = 3+3 = 6,但是由于 "45" 不在有效的译码范围内,所以 f("123") 的结果不能算在内,所以最终结果应该为3。

  • 可见,枚举到某位数字时,此时有多少种译码方式,可以由之前的译码方式相加得出,即当前的状态可以利用之前的状态,这是典型的动态规划。

③状态转移方程:f(x)=f(x-1)+f(x-2)(其中 x 表示数组长度,f(x) 表示有多少种有效的译码方式;是否加上 f(x-1) 取决于 nums[x] 是否在 [1,9] 内,即 nums[x] 需要满足不为0;是否加上 f(x-2) 则取决于 nums[i-1] 与 nums[i] 的数字组合是否在 [10,26] 内)

时间复杂度:O(N) ,需要遍历一次数组

空间复杂度:O(N) ,需要声明一个状态数组记录f(x)

class Solution {
    public int solve (string nums) {
        int[] codesNum=new int[nums.Length]; //状态数组,记录f(x)
        //初始条件:
        if(nums[0].ToString() != "0"){
            codesNum[0]=1;
        }
        //从数组第2位枚举到最后一位:
        for(int i=1;i<nums.Length;++i){
            //判断nums[i]是否不为0
            if(nums[i].ToString() != "0"){
                codesNum[i]+=codesNum[i-1];
            }
            //判断nums[i-1]与nums[i]的数字组合是否在[10,26]内
            int temp=int.Parse(nums[i-1].ToString() + nums[i].ToString());
            if(temp >= 10 && temp <= 26){
                if(i==1){ //即f(2),因为不存在f(0),所以需要特殊处理:
                    codesNum[i]+=1;
                }else{
                    codesNum[i]+=codesNum[i-2];
                }
            }
        }
       return codesNum[nums.Length-1];
    }
}
全部评论
牛逼,写的好好,比官解好多了
1 回复 分享
发布于 2024-06-19 11:41 广东
写的太好了!! 代码逻辑也十分清晰!感谢!!
点赞 回复 分享
发布于 2024-10-16 16:26 湖南
厉害了。
点赞 回复 分享
发布于 2023-03-18 20:32 陕西
推导公式万岁。我发现每次官方的都不是最容易理解的,神奇的官方
点赞 回复 分享
发布于 2023-02-15 22:48 北京
这个解释最到位,前面的都只是记录了一个公式,没有推倒公式怎么来的,看的一脸懵逼
点赞 回复 分享
发布于 2022-10-03 00:04 浙江
竟然没加精?前面的都没有分析状态转移方程怎么来的,看的我难受死了,这个nice
点赞 回复 分享
发布于 2022-08-06 21:29
看懂了,可能是笔误,由于 "34" 不在有效译码的范围内,所以这两种译码方式会被抛弃掉。
点赞 回复 分享
发布于 2022-05-25 15:21
分析的好好,自己感觉分析起来好费力
点赞 回复 分享
发布于 2022-02-25 14:54

相关推荐

评论
40
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务