题目
一条包含字母 A-Z 的消息通过以下方式进行了编码:
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入: "226"
输出: 3
解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decode-ways
思路
这个题有点像爬楼梯,但是比爬楼梯复杂很多。有很多限制条件,甚至题目没给出来,比如不能以0开头,不能连续出现两个0。 总之分解之后的数值只能在[1,26]之间。
先用递归的思想,自顶向下的思考问题。这里的“顶”是指字符串最后一个字符。
如果连续两个数字符合条件([1,26],如果像09这种,就不是两个数字符合条件,因为拆分成两个的话,会出现0和9, 0不符合条件。简单的方式是看这两个字符形成的数字是不是在[10,26]之间。其中10在这之前被考虑过。),
则有两种解法,一种是分成两个数字,还有一种是整个解成一个数字。
考虑限制条件比较麻烦,以下有先后顺序:
- 不能以0开头
- 不能出现连续两个0
- 如果当前数字不是0,则可以先将两个数字拆开解(解法1)
- 如果连续两个数字符合条件,则将两个数字一起解(解法2)
- 其他大于26的情况,只能拆开解,已经在第3步考虑过了
代码
递归
class Solution(object):
def num(self,s, i):
# 不能以0开头
if int(s[0]) == 0:
return 0
if i <= 0: # <是有必要的
return 1
# 不能出现两个0
if i >= 1 and int(s[i] + s[i - 1]) == 0:
return 0
res = 0
# 当前数字不是0
if int(s[i]) != 0:
# 先拆分一步
res = self.num(s, i - 1)
digit = int(s[i - 1] + s[i])
# 如果连续的两位数符合条件,有两种解法
if 10 <= digit <= 26:
res += self.num(s, i - 2)
return res
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
return self.num(s, len(s) - 1)
记忆化搜索
记忆化搜索也很简单,将计算结果保存起来即可。
dp = {0: 1}
class Solution(object):
def num(self, s, i):
if i not in dp:
if i <= 0: # <是有必要的
dp[i] = 1
return 1
# 不能出现两个0
if i >= 1 and int(s[i] + s[i - 1]) == 0:
dp[i] = 0
return 0
res = 0
# 当前数字不是0
if int(s[i]) != 0:
# 则可以拆分连续的两个
res = self.num(s, i - 1)
digit = int(s[i - 1] + s[i])
# 如果连续的两位数符合条件
if 10 <= digit <= 26:
# 可以把连续的两个作为一起
res += self.num(s, i - 2)
dp[i] = res
return dp[i]
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
# 不能以0开头
if int(s[0]) == 0:
return 0
return self.num(s, len(s) - 1)
但是不知道为啥,网站说跑不过测试用例"10",说我输出为2,我手动将测试用例改成"10"输出明明是1:
纠结了一会,不管它。改成动态规划看能过不。
动态规划
采用动态规划自底向上的解决问题,也就是从字符串第一个字符开始考虑。
class Solution:
def numDecodings(self, s: str) -> int:
n = len(s)
if s[0] == '0':
return 0
dp = [1] + [0] * n
# 用i+1替换为i,不然容易数组越界
# i 从[0,n-1]
for i in range(0,n):
# 不能出现两个0
if i >= 1 and int(s[i] + s[i - 1]) == 0:
return 0
# 当前数字不是0
if int(s[i]) != 0:
dp[i+1] = dp[i] # 解法1
if i >= 1 and 10 <= int(s[i - 1] + s[i]) <= 26:
dp[i+1] += dp[i-1] # 解法2
return dp[n]
这里要注意一点的是,本来如果有两个解法是写成dp[i] = dp[i-1]+dp[i-2]
,但是容易数组越界,要考虑的情况很多,不信可以试下。
因此这里令i = i + 1
,问题就好处理了,但是处理s
时还是一样的。
被我刷到了28ms ^v^