题目
给定一个数字,我们按照如下规则把它翻译为字符串:
0 翻译成 a
,1 翻译成 b
,……,11 翻译成 l
,……,25翻译成 z
。
一个数字可能有多个翻译。
例如 12258 有 5 种不同的翻译,它们分别是 bccfi
、bwfi
、bczi
、mcfi
和 mzi
。
请编程实现一个函数用来计算一个数字有多少种不同的翻译方法。
数据范围
输入数字位数 [1,101]。
样例
输入:"12258"
输出:5
代码和思路
看到这个题我的第一反应是递归,那就拿出我的递归三板斧:
- 假设这个递归你已经写完了,能直接用。
- 分析问题,看怎么拆分成子问题
- 找到特殊的结束递归点
思路
递归的函数要干什么?
假设递归写完的前提是你得知道你写的函数是做什么的。对这道题直接就是题目问的东西了:
输入一串数字的字符串,输出他翻译方法的种类数。
这样我们就直接假设我们已经实现这个功能了,我们来具体看看怎么拆分成子问题。
拆分子问题
因为翻译范围是0~25,所以对一个字符串拆分就两种方式:
- 把当前字符串的第一个字符翻译为字母,之后再把剩下的字符串递归。
- 把当前字符串的前两个字符翻译为字母,之后再把剩下的字符串递归。
第一种情况很简单,0~9都有对应的字母,所以无论开头的是什么数字都可以直接翻译。
复杂的是第二种情况。下面我整理了下:
- 当前两个字符组成的数字大于25,意味着没有对应的字母能翻译。这时不用对下面的数进行递归,直接为0
- 当开头的第一个数字为0(01,02…)时,也没有对应的字母能翻译。这时不用对下面的数进行递归,直接为0
这里需要一个把string
转换为int
的功能,我们写一个函数:
int toInt(string str) {
int len = str.length(), ans = 0;
for (int i = 0; i < len; i++) {
ans *= 10;
ans += str[i] - '0';
}
return ans;
}
之后依据我们总结的条件能写出以下代码:
int getTranslationCount(string s) {
int first = 0, second = 0;
first = getTranslationCount(s.substr(1)); // 第一个字符
// 前二个字符
second = (s[0] == '0' || toInt(s.substr(0, 2)) > 25) ? 0 : getTranslationCount(s.substr(2));
return first + second;
}
结束递归的条件
当这个递归进行到最下的时候大概有3种特殊情况:
- 传入递归的字符串长度为0
- 传入递归的字符串长度为1
- 传入递归的字符串长度为2
第一种情况和第二种情况很简单,字符串长度为0就没得翻译,返回0;字符串长度为1就只有1种翻译方法,返回1。
当第三种情况时,可能翻译的方法有两种,一种翻译成两个字母,一种翻译成一个字母。
if (s.length() == 0) return 0;
if (s.length() == 1) return 1;
if (s.length() == 2) {
if (S[0] == '0' || toInt(s) > 25) return 1;
return 2;
}
代码
class Solution {
public:
int toInt(string str) {
int len = str.length(), ans = 0;
for (int i = 0; i < len; i++) {
ans *= 10;
ans += str[i] - '0';
}
return ans;
}
int getTranslationCount(string s) {
if (s.length() == 0) return 0;
if (s.length() == 1) return 1;
if (s.length() == 2) {
if (s[0] == '0' || toInt(s) > 25) return 1;
return 2;
}
// s长度一定大于2
int first = 0, second = 0;
first = getTranslationCount(s.substr(1));
second = (s[0] == '0' || toInt(s.substr(0, 2)) > 25) ? 0 : getTranslationCount(s.substr(2));
return first + second;
}
};