这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战
问题描述
剑指 Offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例:
输入:12258
输出:5
解释:12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"。
分析问题
对于一个数字的每一位 num[i] 来说,它有两种选择。
- 它作为单独的一部分进行翻译。
- 它和它前面的一位作为一个整体来进行翻译(要求其范围为10~25)。
假设给定的数 nums= x1x2x3x4...xi-2xi-1xi...xn,数x1x2x3x4...xi-2的方案数为f(x-2),数x1x2x3x4...xi-2xx-1的方案数是f(x-1)。
- 当xi-1xi整体翻译时,x1x2x3x4...xi-2xi-1xi的整体方案是f(i-2)。
- 当xi单独翻译时,x1x2x3x4...xi-2xi-1xi的整体方案是f(i-1)。
所以可以得出:
f(i) =
有了递推关系,就可以使用动态规划来求解。我们用 dp[i] 代表以 xi 为结尾的数字的翻译方案数量。则根据递推关系我们可以求出状态转移方程为:
dp[i] =
其中要想xx-1xi可以被翻译,就需要其属于[10,25]。
下面我们来看一下边界条件,对于只有一位数字来说,它只能有1种翻译方案,故dp[1]=1,当有两位数字时,如果其属于[10,25],我们可以知道其有2种翻译方案,所以dp[2]=2,又因为dp[2]=dp[1]+dp[0],故dp[0]=1。
我们来看一个具体的例子,假设输入是12258。
因为dp[i]只依赖于dp[i-2]和dp[i-1],所以我们只需要申请两个变量就可以求解,从而省去dp的空间。
下面我们来看一下代码如何实现。
class Solution:
def translateNum(self, num):
#想将num转成字符串,方便获取xi-1xi
s = str(num)
a = b = 1
for i in range(2, len(s) + 1):
if "10" <= s[i - 2:i] <= "25":
temp=a+b
else:
temp=a
b = a
a = temp
return a
该算法的时间复杂度是O(n),空间复杂度也是O(n)。
优化
这道题我们也可以采用数学求余的方法来求解。具体来说。
我们可以利用求余运算 num % 10 和求整运算 num // 10 ,来获取数字num的每一位数字(获取顺序为个位、十位、百位…)。因此,可通过求余和求整运算实现从右向左的遍历计算,从而把算法的空间复杂度减低为O(1)。
class Solution:
def translateNum(self, num):
a = b = 1
y = num % 10
while num != 0:
num //= 10
x = num % 10
tmp = 10 * x + y
if 10 <= tmp <= 25:
c = a + b
else:
c = a
b=a
a=c
y = x
return a
该算法的时间复杂度是O(n),空间复杂度是O(1)。