1. 题目来源
链接:把数字翻译成字符串
来源:LeetCode——《剑指-Offer》专项
2. 题目说明
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a”
,1 翻译成 “b”
,……,11 翻译成 “l”
,……,25 翻译成 “z”
。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”
提示:
0 <= num < 2^31
3. 题目解析
方法一:思维+递归+巧妙解法
题意很明确,求方法总数,以下几点为主要思路:
- 以 12258 为例,观察数字 1 可发现它能翻译构成两种情况
- 第一种:单独翻译构成字符
'b'
,后面剩下数字 2258 - 第二种:与下一位数字 2 进行有效结合构成字符 ‘m’,后面剩下数字 258
- 第一种:单独翻译构成字符
- 不难发现,从后往前的进行处理,可以写成递归函数的形式。即定义 f ( i ) = f ( i + 1 ) + g ( i , i + 1 ) × f ( i + 2 ) f(i)=f(i+1)+g(i,i+1)\times f(i+2) f(i)=f(i+1)+g(i,i+1)×f(i+2),其中 f ( i ) f(i) f(i)表示从第 i i i位数字开始的翻译数目的总和。当第 i i i 位与第 i + 1 i+1 i+1 位两位拼接翻译起来的数字在范围 10~25 之间时, g ( i , i + 1 ) g(i,i+1) g(i,i+1)值为 1 ,否则为 0。
大致梳理程序设计思路:
- 首先需要将数字
num
采用to_string
的形式转化为字符串,便于对每一位进行处理 - 申请一个动态数组,长度等于数字位数,用于记录 f ( i ) f(i) f(i)
- f ( 0 ) = 1 f(0)=1 f(0)=1,即原数字就为最初始的一种情况
- 若当前位数字不能有效结合下一位数字构成新情况时,此时情况总数不变,即 f ( i ) = f ( i + 1 ) f(i)=f(i+1) f(i)=f(i+1)
- 若当前位与下一位进行有效结合,那么相当于字符串第
i
位与第i+1
位构成了一个字符替代了第i+1
位的字符,也就是作为第i+2
位数字及其以后所有数字的 新头,那么就需要添加 f ( i + 2 ) f(i+2) f(i+2) 的所有情况,这也是递归、迭代的核心思想,数字的结合仅影响该数字后面的所有数字的情况,这个需要理解
上面就是很直观的递归思路,但是这个递归是 自上而下 进行处理的,会产生中间大量的重复运算。将其修改为 自下而上 的迭代解法,从后向前进行处理,可大大提高效率。
其实能够发现这个递归式子很类似于跳台阶问题,也可以从这个方面进行思考!
参见代码如下:
// 执行用时 :0 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :8.3 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
int translateNum(int num) {
string tmp = to_string(num);
vector<int> counts(tmp.size(), 0);
int count = 0;
for (int i = tmp.size() - 1; i >= 0; --i) {
count = 0;
if (i < tmp.size() - 1) count = counts[i + 1];
else count = 1;
if (i < tmp.size() - 1) {
int cnt = (tmp[i] - '0') * 10 + tmp[i + 1] - '0';
if (cnt >= 10 and cnt <= 25) {
if (i < tmp.size() - 2) count += counts[i + 2];
else count += 1;
}
}
counts[i] = count;
}
return counts[0];
}
};