剑指Offer_#46_把数字翻译成字符串

剑指Offer_#46_把数字翻译成字符串

Contents

题目

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

示例 1:

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

提示:

0 <= num < 231

思路分析

题意的核心在于,如果数字当中的某两位组成的数字在10~25的范围内,我们就有这两位数字就会有两种翻译。例如12258当中的前2位,即12,可以翻译为

  • bc(1和2分别翻译)
  • m(12作为整体来翻译)

这是一个递归问题。可以这样理解,假设数字最后一位的索引是n,递归问题就是recur(n),即求解以索引n 为结尾的数字的翻译方式。
这个问题可以分解为递归子问题,且分解的过程需要进行分类讨论:

  • 如果nn-1位置的数字组合起来在10~25范围内:
    • recur(n) = recur(n-1) + recur(n-2)
  • 如果nn-1位置上的数字组合起来不在10~25范围内:
    • recur(n) = recur(n-1)

看这个递归形式,有没有觉得似曾相识?这一题其实是跳台阶一题的扩展。引用评论区大佬一句很好的总结:

  • 《斐》、《青蛙》、《翻译》,三道题,难度越来越大,为什么呢?并不是算法难了,而是题目变得抽象了。
  • 《斐》和《青蛙》是无条件的往后跳,而《翻译》是有条件的往后跳,我们要做的就是找到需要的判断条件(即[10, 25])。

明白了上面说的,其实代码编写与跳台阶一题也很相似,仅仅是增加了一个条件判断。
我们通过递归的思维从上到下的分析问题,编写代码时,为了避免重复计算,可以从下到上地解决问题,即动态规划,从dp[0]开始计算,直到dp[n],就可以得到结果。

解答

class Solution {
    public int translateNum(int num) {
        //先将数字转换为字符串
        String str = String.valueOf(num);
        //注意dp数组和字符串是错位的,多出一个dp[0]
        int[] dp = new int[str.length() + 1];
        //初始化动态规划dp数组
        dp[0] = 1;
        dp[1] = 1;
        //根据dp状态转移方程进行迭代
        //这里的索引值最大是字符串长度,因为substring()方法不包含结束索引
        for(int i = 2;i <= str.length();i++){
            String tmp = str.substring(i - 2, i);
            if(tmp.compareTo("10") >= 0 && tmp.compareTo("25") <= 0)
                dp[i] = dp[i - 1] + dp[i - 2];
            else 
                dp[i] = dp[i - 1];
        }
        //返回最后一个元素
        return dp[str.length()];
    }
}

dp数组与数字字符串的对应关系
注意dp[]的索引与字符串的索引是相差1的。

dp[0]的推导
当前两位数字在10~25范围内时,有dp[2] = dp[1] + dp[0],其中dp[2] = 2,dp[1] = 1,所以dp[0] = 1

posted @ 2020-07-17 11:04  Howfar's  阅读(144)  评论(0编辑  收藏  举报