一条包含字母 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 343:整数拆分(最详细的解法!!!)
Leetcode 279:完全平方数(最详细解决方案!!!)
我们当然可以使用最短路径算法来解这个问题,但是我们这里不再赘述。我们首先想到的解法依旧是通过递归来解。输入字符串s
有多少个编码,是由这样三个子问题决定的:
s[0]和s[0:2]
都合法,那么f(s[:])=f(s[1:])+f(s[2:])
s[0]
合法而s[0:2]
不合法的话,那么f(s[:])=f(s[1:])
s[0]
都不合法,那么不可能存在编码所以我们很快写出下面这种代码
class Solution:
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
if len(s) == 1:
return 1
result = 0
if 1 <= int(s[0]) <= 9:
result += self.numDecodings(s[1:])
if len(s) >= 2 and 10 <= int(s[0:2]) <= 26:
result += self.numDecodings(s[2:])
return result
但是上面这种写法是错的,很容易验证上面的做法,例如输入11
,输出为1。
错误的问题在于没有真正理解边界条件是什么?这里我们的边界条件应该分情况讨论
s
为空,返回0
s
不为空,我们在递归的过程中,递归到了s
的末尾了,此时应该返回1
class Solution:
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
return self._numDecodings(s)
def _numDecodings(self, nums):
if not nums:
return 1
result = 0
if 1 <= int(nums[0]) <= 9:
result += self._numDecodings(nums[1:])
if len(nums) >= 2 and 10 <= int(nums[0:2]) <= 26:
result += self._numDecodings(nums[2:])
return result
这个代码我们可以通过记忆化搜索的方法进行优化。
class Solution:
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
mem = [0 for _ in s]
return self._numDecodings(s, 0, mem)
def _numDecodings(self, nums, index, mem):
if index == len(nums):
return 1
if mem[index] != 0:
return mem[index]
if 1 <= int(nums[index]) <= 9:
mem[index] += self._numDecodings(nums, index + 1, mem)
if index + 1 < len(nums) and 10 <= int(nums[index: index +2]) <= 26:
mem[index] += self._numDecodings(nums, index + 2, mem)
return mem[index]
同样这个问题我们也可以通过动态规划的方法求解。这里的思路和上面的解法是一样的,但是这里我们要考虑的问题更多。首先我们要排除输入字符串为空和'0'
开头的可能性,另外对于len(s)==1
的情况我们要单独考虑。实际上这个问题是一个变相的斐波那契数列,状态方程
f(i)=f(i-1)(where s[i] valid)+f(i-2)(where s[i-1:i+1] valid) i>=2
我们的初值根据是否为'0'
做确定,如果为'0'
,设为0
,否则的话设为1
。我们在建立存储空间的时候要多建立一个单元,因为mem[1]=mem[0]+mem[-1]
,而在这里我们无法表示-1
这个概念,所以只有通过将mem
长度向后延申的办法实现,举个例子
s: 1 2 1 2 0
mem: 1 1 2 3 5 3
上面例子中,由于s[0]=1 s[1]=2
,所以我们将mem[0]=1 mem[1]=1
。
class Solution:
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
if not s or s.startswith('0'):
return 0
if len(s) == 1 and s[0] != '0':
return 1
s_len = len(s)
mem = [0 for _ in range(s_len + 1)]
mem[0] = 1 if s[0] != '0' else 0
mem[1] = 1 if s[1] != '0' else 0
for i in range(2, s_len + 1):
if 1 <= int(s[i - 1]) <= 9:
mem[i] += mem[i - 1]
if 10 <= int(s[i-2:i]) <= 26:
mem[i] += mem[i - 2]
return mem[-1]
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!