把数字翻译成字符串(Python and C++解法)
题目:
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof
思路:
可使用递归或动态规划解决该问题,它们的区别:
- 递归是自上而下解决问题,然后等待从下面返回上来的结果;
- 动态规划是自下而上解决问题,从已知的 base case 出发,存储前面的状态,迭代出最后的结果;
此处使用动态规划解法:
定义状态:以第 i 位结尾的前缀串翻译的方案数。
转移方程思路:假设翻译字符串的第 i 位置:可以单独作为一位来翻译;如果第 i−1 位和第 i位组成的数字在 10 到 25 之间,可以把这两位连起来翻译。可以用 f(i) 表示以第 i 位结尾的前缀串翻译的方案数,然后考虑第 i 位单独翻译和与前一位连接起来再翻译对 f(i)的贡献。单独翻译对 f(i)的贡献为 f(i - 1);如果第 i−1 位存在,并且第 i−1 位和第 i位形成的数字 x满足 10≤x≤25(如00, 01, 02,35, 99无法被翻译),那么就可以把第 i−1 位和第 i位连起来一起翻译,对 f(i)的贡献为 f(i - 2),否则为 0。
注意1:
如果只翻译自己,比如 12345,如果 5单独翻译,那么方法数与 1234 是一样的,dp(i)=dp(i−1)。
注意2:
由于当前 dp 项只和它前面两项有关,无需用数组存储所有的 dp 项,可用变量去存状态值,在迭代中更新变量,这样空间复杂度优化到 O(1),在C++解法中采用这种做法。另外,由于翻译的对称性,且C++中string的只能取从第i位开始向后的子串,所以采用从右向左的翻译顺序。(python解法中为从左向右)
Python解法:
1 class Solution: 2 def translateNum(self, num: int) -> int: 3 stred = str(num) # 数字转字符串方法,str(2) = '2' 4 Len = len(stred) 5 dp = [0] * (Len+1) # 备忘录,避免重复计算 6 dp[0] = 1 7 dp[1] = 1 8 for i in range(2, Len+1): # 从左到右翻译 9 temp = int(stred[i-2] + stred[i-1]) # 字符串转数字方法,int('2') = 2 10 if temp >= 10 and temp <= 25: 11 dp[i] = dp[i-1] + dp[i-2] 12 else: 13 dp[i] = dp[i-1] 14 return dp[Len]
C++解法:
1 class Solution { 2 public: 3 int translateNum(int num) { 4 string stred = to_string(num); // C++数字转字符串方法 5 int prepredpi = 1, predpi = 1, dpi; 6 if(stred.size() == 1) 7 return 1; 8 for(int i = stred.size()-2; i >= 0; i--) { // 从右向左翻译 9 string preSubstr = stred.substr(i, 2); // 抽取从i位置开始向后的2个字符的子串 10 if(preSubstr >= "10" && preSubstr <= "25") 11 dpi = predpi + prepredpi; 12 else 13 dpi = predpi; 14 prepredpi = predpi; 15 predpi = dpi; 16 } 17 return dpi; 18 } 19 };