Leetcode 91:解码方法(最详细的解法!!!)

一条包含字母 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

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)