给定数字类型的字符串,转化为字母类型的字符串,转化的方法数是多少

给定数字类型的字符串,转化为字母类型的字符串,转化的方法数是多少?

提示:暴力递归,Facebook考题


题目

给定数字类型的字符串s,转化为字母类型的字符串,转化的方法数是多少?
转化规则是:
1–26对应A–Z字符


一、审题

示例:比如:
111=s,有多少种转化方法,变成字母字符串?

1 1 1=AAA【每个1单独变】
1 11=AK【11组合】
11 1=KA

111能转化吗?不能,因为超过26了
数字只能组合为1位或者2位


二、解题

咱们从左往右决定i位置的数字,是单独转化呢?还是联合i+1凑成对来转化?

下面是转化规则:
在这里插入图片描述
定义暴力递归f(str,i):目前str[0–i-1]范围内的已经转化好了,请问你i–N-1范围上的字符继续转化,有多少种方案?
在这里插入图片描述
(1)遇到0,不好意思,整个方案不行,转化方法为0;
(2)遇到i来到了N位置呢?越界了,说明刚刚好,整个方案可以搞定。返回1
(3)来到任意位置i,其实i可以单独走,也可以配合i+1组合成2位数字,当然不能再用i+2组合成3位,因为最大26哇
这里面就分为几种情况:
1)i位置是1,那么可以单独转化A【下一次直接去探索i+1】,i+1还不越界的话,i和i+1可以组合为10–19【下一次直接去探索i+2】
所以结果可以是这俩的和:f(i+1)+f(i+2),然后返回;
2)i位置是2,那么可以单独转化B【下一次直接去探索i+1】,i+1还不越界的话,i和i+1可以组合为20–29,注意,只能是20–26哦,其他非法的,所以限定i+1必须在0–6之间【下一次直接去探索i+2】
所以结果可以是这俩的和:f(i+1)+f(i+2),然后返回;
3)i位置是3–9,没啥可以说,他们只能单独转化为C–I,没法组合出30++来,只能是1–26【下一次直接去探索i+1】
所以结果只能是
f(i+1)

这就是我们的递归,主函数怎么调用?
直接调用f(str,0),让你求str的0–N-1位置上有多少种转化方案,就是原题的意思

手撕代码:

//复习:
    //目前str[0--i-1]范围内的已经转化好了,请问你i--N-1范围上的字符继续转化,有多少种方案?
    public static int f(char[] str, int i){
        int N = str.length;
        //(2)遇到i成功地来到了N位置呢?越界了,说明刚刚好,整个方案可以搞定。返回1
        if (i == N) return 1;//这个条件得在前面,越界了就OK

        //(1)遇到0,不好意思,整个方案不行,转化方法为0;
        if (str[i] == '0') return 0;
        //(3)来到任意位置i,其实i可以单独走,也可以配合i+1组合成2位数字,当然不能再用i+2组合成3位,因为最大26哇
        //这里面就分为几种情况:
        int ans = 0;
        //1)i位置是1,那么可以单独转化A【下一次直接去探索i+1】,i+1还不越界的话,i和i+1可以组合为10--19【下一次直接去探索i+2】
        //所以结果可以是这俩的和:**f(i+1)+f(i+2)**,然后返回;
        if (str[i] == '1'){
            ans += f(str, i + 1);//1独立转化,当然你可以存下结果
            if (i + 1 < N) ans += f(str, i +2);//i+1还没越界,可以考虑组合1x
        }
        //2)i位置是2,那么可以单独转化B【下一次直接去探索i+1】,i+1还不越界的话,i和i+1可以组合为20--29,注意,只能是20--26哦,其他非法的,所以限定i+1必须在0--6之间【下一次直接去探索i+2】
        //所以结果可以是这俩的和:**f(i+1)+f(i+2)**,然后返回;
        else if (str[i] == '2') {
            ans += f(str, i + 1);//1独立转化,当然你可以存下结果
            if (i + 1 < N && str[i + 1] >= '0' && str[i + 1] <= '6')
                ans += f(str, i + 2);//i+1还没越界,可以考虑组合20--26
        }
        //3)i位置是3--9,没啥可以说,他们只能单独转化为C--I,没法组合出30++来,只能是1--26【下一次直接去探索i+1】
        //所以结果只能是**f(i+1)**
        else ans += f(str,i + 1);

        return ans;
    }
    //直接调用f(str,0),让你求str的0--N-1位置上有多少种转化方案,就是原题的意思
    public static int howManyWaysConvert(String s){
        if (s.compareTo("") == 0 || s.length() == 0) return 0;

        char[] str = s.toCharArray();
        return f(str, 0);
    }

    public static void test(){
        String s = "111";
        System.out.println(numToCharacter1(s));
        System.out.println(howManyWaysConvert(s));
    }
3
3

暴力递归转动态规划代码

这里f就一个变量i,定义为:探索str的i–N-1范围上有多少种转化方案?
主函数是f(str,0),从0–N-1位置,最后不断分,直到i==N-1时,拿到结果,一层一层返回,最求了f(0)

干脆咱们用一个数组dp去存这个结果,dp长度为N+1,因为i=N时,返回结果是1
在这里插入图片描述
dp[i]定义为为:探索str的i–N-1范围上有多少种转化方案
咱最终要求的就是dp[0],要f不断递归去找,到终止条件:dp[N]=1,然后返回来地推dp[0]
这大概就是动态规划的意思

代码咋写呢?完全根据上面的暴力递归写代码:

//复习:
    //dp[i]定义为为:**探索str的i--N-1范围上有多少种转化方案**
    public static int howManyWaysConvertDP(String s){
        if (s.compareTo("") == 0 || s.length() == 0) return 0;

        int N = s.length();
        //一个变量i,根据f(str,i)来改编
        int[] dp = new int[N +1];
        char[] str = s.toCharArray();

        //(2)遇到i成功地来到了N位置呢?越界了,说明刚刚好,整个方案可以搞定。返回1
        dp[N] = 1;
        //然后填表,从右往左填表,递归dp[0]
        for (int i = N - 1; i >= 0; i--) {
            //(1)遇到0,不好意思,整个方案不行,转化方法为0;
            if (str[i] == '0') dp[i] = 0;
            //(3)来到任意位置i,其实i可以单独走,也可以配合i+1组合成2位数字,当然不能再用i+2组合成3位,因为最大26哇
            //这里面就分为几种情况:
            int ans = 0;
            //1)i位置是1,那么可以单独转化A【下一次直接去探索i+1】,i+1还不越界的话,i和i+1可以组合为10--19【下一次直接去探索i+2】
            //所以结果可以是这俩的和:**f(i+1)+f(i+2)**,然后返回;
            if (str[i] == '1'){
                ans += dp[i + 1];//1独立转化,当然你可以存下结果
                if (i + 1 < N) ans += dp[i +2];//i+1还没越界,可以考虑组合1x
            }
            //2)i位置是2,那么可以单独转化B【下一次直接去探索i+1】,i+1还不越界的话,i和i+1可以组合为20--29,注意,只能是20--26哦,其他非法的,所以限定i+1必须在0--6之间【下一次直接去探索i+2】
            //所以结果可以是这俩的和:**f(i+1)+f(i+2)**,然后返回;
            else if (str[i] == '2') {
                ans += dp[i + 1];//1独立转化,当然你可以存下结果
                if (i + 1 < N && str[i + 1] >= '0' && str[i + 1] <= '6')
                    ans += dp[i + 2];//i+1还没越界,可以考虑组合20--26
            }
            //3)i位置是3--9,没啥可以说,他们只能单独转化为C--I,没法组合出30++来,只能是1--26【下一次直接去探索i+1】
            //所以结果只能是**f(i+1)**
            else ans += dp[i + 1];

            dp[i] = ans;//把可能的情况赋值给它
        }

        return dp[0];//这就是我们要的结果,str0--N-1范围上的可能转化结果。
    }

    public static void test2(){
        String s = "111";
        System.out.println(numToCharacter2(s));
        System.out.println(howManyWaysConvertDP(s));
    }

    public static void main(String[] args) {
//        test();
        test2();
    }
3
3

这么做的好处是啥呢?
比如你求f(9),你每次都要重复去求f(10),f(11),……,f(N-1)
如果当初f(10)已经通过dp[10]保存好了,就不用再次递归求f(11),……,f(N-1)了
这样节省了大量的递归时间。
这就是动态规划的本意。


总结

提示:重要经验:

1)从左往右的尝试,就是看i位置的情况如何,来做决定,决定下一次要去i+1还是i+2,以后经常又题目会这么分析。
2)暴力递归可以转化为动态规划的代码,所谓动态规划,就是在递归求规模更小时,直接保存结果,然后在更大的规模调用递归时直接获取下面的结果,不至于反复递归求到底。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰露可乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值