面试题46. 把数字翻译成字符串(递归 + 动态规划)

题目

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”

方法一 递归

这道题我最先想到的方法是递归,从第一个数开始,每次取当前数字或先后取一个组成两位数,如果能够成功取到最后一位数,就将结果数加一,递归的返回条件就是索引到最后一个数组处。

具体分析,首先将数字转换为字符串,以便于截取其中的一位或两位。
**递归初始值:**由数字组成的字符串s,递归开始的索引idx,两个参数组成,idx从0开始递归。
**递归方式:**当索引idx的值小于s的长度时才继续向下递归,递归包含两种情况,一种是当前数字,递归向下,idx+1;另外一种是包含两个数字,且这个两位数在10~25之间,递归向下,idx+2。
递归结束条件: 当idx的值为字符串s的长度时,说明此时正好取到了最后一位数,res的值自增1,返回上一层。0

代码1

class Solution {
    int res = 0;
    public int translateNum(int num) {
        String s = String.valueOf(num);
        dfs(s,0);
        return res;
    }
    public void dfs(String s,int idx){
        if(idx == s.length()){
            res++;
            return;
        }
        //递归1个数字
        if(Integer.parseInt(s.substring(idx,idx+1) ) < 26) 
            dfs(s,idx+1);
        //递归2个数字
        if(idx < s.length()-1  && Integer.parseInt(s.substring(idx,idx+2)) < 26  && Integer.parseInt(s.substring(idx,idx+2)) >= 10) //当idx小于s的长度减1时才能选取substring,并且数字在10~25之间才递归
            dfs(s,idx+2);   
    }  
}

复杂度
时间复杂度:O(N),递归从头到尾一遍遍历。
空间复杂度:O(1),没有额外空间。

注意点
递归特别注意结束条件,以及在进行选择了两个数字的下次递归,防止索引越界。

方法二 动态规划

刚开始我还没想到用动态规划,通过递归解答完毕后看了一下其他人的思路才发现,动态规划解题更好理解,会觉得本题更简单。下面看看动态规划的几个步骤。

状态定义:定义数组dp,dp[i]为前i个位置的数字翻译法数。那么一定存在一定关系,可以通过前面已知值得到当前值;
状态转移方程:有两种情况,当当前数字和前一个数字组成的一个两位数数字的范围在10~25时,dp[i] = dp[i-1] + dp[i-2],其他情况则是dp[i] = dp[i-1];
初始化:首先需要初始dp[0] = dp[1] = 1;
输出值:dp[length]即为要求的结果。

代码2

class Solution {
	public int translateNum(int num) {
        String s = String.valueOf(num);
        int[] dp = new int[s.length()+1];
        dp[0] = 1;dp[1] = 1;
        for(int i = 1;i<s.length();i++){
            String str = s.substring(i-1,i+1);
            //if(Integer.parseInt(str) >=10 && Integer.parseInt(str) < 26)
            if(str.compareTo("10") >= 0 && str.compareTo("25") <= 0)
                dp[i+1] = dp[i-1] +dp[i];
            else 
                dp[i+1] = dp[i];
        }
        return dp[s.length()];
    }
}    

复杂度
时间复杂度:O(N),空间复杂度:O(N),引入了数组存放动态值。

由于dp[i]和dp[i-1] 和dp[i-2]有关系,可以通过常数的变量记录dp[i-1]和dp[i-2]的值,每次不断更新。
所以通过cur记录当前位置的翻译方法数,pre记录前一个位置的翻译方法数,prepre记录pre的前一个翻译方法数,每次计算完了当前cur的值,更新prepre为pre的值,而pre的值为cur的值进行下一次循环,直到循环到最后一个数字,返回cur即为最总结果。

代码3

class Solution {
	 public int translateNum(int num) {
        String s = String.valueOf(num);
        int prepre = 1,pre = 1,cur = 1;
        for(int i = 1;i<s.length();i++){
            String str = s.substring(i-1,i+1);
            //if(Integer.parseInt(str) >=10 && Integer.parseInt(str) < 26)
            if(str.compareTo("10") >= 0 && str.compareTo("25") <= 0){
                cur = prepre + pre;
            }else{
                cur = pre;
            }
            prepre = pre;
            pre = cur;
        }
        return cur;
    }
}    

复杂度
时间复杂度:O(N),空间复杂度:O(1).

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值