把数字翻译成字符串

题目描述

来源于 https://leetcode-cn.com/

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

示例 1:

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

提示:

  • 0 <= num < 231

思路:

先把数字 num 转换为字符串 s,设 f(i)s[i:] 可以构成的翻译结果数量。那么:

s[i] 可以翻译成一个字母,它与 s[i] 后面的字符串翻译的结果组合起来,并没有带来多样性。但如果 s[i:i+2] 构成的数字在 10~25 之间,它们就可以合并起来构成一个字母。此时:f(i) = f(i+1) + f(i+2),否则 f(i) = f(i+1)

解法一:带记忆的递归

class Solution {
public:
    int translateNum(int num) {
        unordered_map<int, int> cache;
        string s = to_string(num);
        return translateNum(s, 0, cache);
    }
private:
    int translateNum(string s, int i, unordered_map<int, int>& cache){
        if(cache.find(i) != cache.end()){
            return cache[i];
        }
        if(s.size() == i){
            return 1;
        }
        int num = translateNum(s, i+1, cache);
        if(i + 1 < s.size()){
            int n = (s[i] - '0') * 10 + (s[i+1] - '0');
            if(n <= 25 && n >= 10){
                num += translateNum(s, i+2, cache);
            }
        }
        cache[i] = num;
        return num;
    }
};

解法二:自底向上,非递归解法

基本上每个动态规划问题都可以用记忆化递归来解决,递归自顶向下计算,为了避免对子问题重复计算,可以保存下已经计算过的结果。如果先把子问题计算出来,然后在用子问题的结果来结果更大的子问题,这样就可以使用迭代的方法。

本题中依赖的子问题的解只有两个,即 f(i+1)f(i+2),因此可以使用两个变量保存这两个解。产生新的解以后,再更新这两个变量。

class Solution {
public:
    int translateNum(int num) {
        string s = to_string(num);
        int first = 1, second = 1;
        int n1 = num % 10, n2 = 0;
        num /= 10;

        while(num){
            n2 = n1;
            n1 = num % 10;
            n2 += n1 * 10;
            num /= 10;

            int old_first = first;
            if(n2 <= 25 && n2 >= 10){
                first += second; 
            }
            second = old_first;
        }   
        return first;
    }
};