leetcode刷题(83)——面试题46. 把数字翻译成字符串

对于题目的理解,其实也不难,我们并没有必要把数字真的转换成它要求的字母,只要得出有多少种分割方法就行了。

这种分割的问题也叫“隔板问题”——在数字之间的缝隙里插入隔板,看有多少种分法,是一类组合问题。这里由于受到26个字母的限制,只需要考虑分割之后,每两个“隔间”内有两个数字就可以了。也就是说,我们只需要考虑当前数字与它后面的数字的组合是不是在[10,25]内即可,这里之所以是边界是10,因为会出现01,02这种,这种不是有效的2位数,不能转化为对应的字母

这么想的话,实际上该问题已经有点像经典的“爬楼梯”问题了:一次上一阶或者两阶,总共有多少种走法。于是自然想到了用动态规划的解法,关键性的问题也就出现了:递推公式是什么?

首先我们一定能想到不复杂的情况:

只有一个数字时,不用切割,只有一种对映,dp[0] = 1;
dp[1]的值是多少实际上取决于字符串str[1]和str[0]构成的数字是不是处于[0,25]区间内——是,则dp[1] = 2;否则dp[1] = 1.
这两条是最容易想到的,不过也确实为后面的条件判断打下基础了。接下来是数学中的归纳法,通俗说是找规律。以题目中的输入示例12258来看,dp[0] = 1, dp[1] = 2

dp[2] = 3((1,22), (12,2), (1,2,2))
dp[3] = 5 ((12,25), (1,2,25), (1,22,5), (12,2,5),(1,2,2,5))
dp[4]还是5,原因是str[4] = 8与str[3] = 5组合起来大于25,不能对映到一个字符,5和8之间必须切开,所以加上的这个数字8并不影响分割方式。
好了,上面的例子写完,如果还没找到规律可以接着往后写。对于dp问题敏感的选手应该已经想到需要用上dp[i], dp[i-1]这些元素来总结递推了:

2 <= i < len(str)
if str[i]和str[i-1]构成的数字 < 26:
    dp[i] = dp[i-1] + dp[i-2]   #等于前两项之和
else:
    dp[i] = dp[i-1]             #不受影响直接等于前一项

代码

public class Solution46m {
    public int translateNum(int num) {
        String res = String.valueOf(num);
        int[] dp = new int[res.length()];
        if (res.length() < 2) {
            return res.length();
        }
        dp[0] = 1;
        int firstSum = Integer.valueOf(res.substring(0, 2));
        dp[1] = firstSum >= 10 && firstSum <= 25 ? 2 : 1;
        for (int i = 2; i < res.length(); i++) {
            int sum = Integer.valueOf(res.substring(i - 1, i + 1));
            if (sum >= 10 && sum <= 25) {
                dp[i] = dp[i - 1] + dp[i - 2];
            } else {
                dp[i] = dp[i - 1];
            }
        }
        return dp[res.length()-1];
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章