Leetcode 44:通配符匹配(超详细的解法!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:coordinate@live.com https://blog.csdn.net/qq_17550379/article/details/84191382

给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?''*' 的通配符匹配。

'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。

两个字符串完全匹配才算匹配成功。

说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 ?*

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。

示例 3:

输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。

示例 4:

输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".

示例 5:

输入:
s = "acdcb"
p = "a*c?b"
输入: false

解题思路

这个问题和之前问题Leetcode 10:正则表达式匹配(最详细的解法!!!)很类似,而且比之前的问题要容易,我们只要在之前问题上稍加修改就可以了。我们稍微说一下这个问题的思路,这个问题的难点在于判断*匹配多少次的问题。所以我们不妨从最简单的情况开始考虑,我们首先判断p的第一个元素是不是*。如果p的第一个元素是*的话,那我们只要考虑*是匹配零次还是匹配一次即可,也就是我们只要判断isMatch(s[1:],p)isMatch(s,p[1:])

如果p的第一个元素不是*的话,我们只要判断s[0]p[0]能否匹配。所以我们可以非常迅速的写出动态规划转移方程。

  • f(i,j)=f(i,j1) or f(i1,j)  if  p[j1]=f(i,j)=f(i,j-1)\ or\ f(i-1, j)\ \ if\ \ p[j-1]='*'
  • f(i,j)=f(i1,j1)  and  (s[i1]==p[j1]p[j1]==?)  if  p[j1]f(i,j)=f(i-1,j-1)\ \ and \ \ (s[i-1]==p[j-1] || p[j-1]=='?')\ \ if\ \ p[j-1]\neq'*'

f(i,j)表示输入s[0:i]和输入p[0:j]时的匹配结果。具体的思维转换过程可以阅读之前的文章。

class Solution:
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        s_len, p_len = len(s), len(p)
        mem = [[False]*(p_len + 1) for _ in range(s_len + 1)]
        mem[0][0] = True
        for i in range(s_len + 1):
            for j in range(1, p_len + 1):
                if p[j-1] == '*':
                    mem[i][j] = mem[i][j-1] or \
                                (i > 0 and mem[i-1][j])
                else:
                    mem[i][j] = i > 0 and\
                                mem[i-1][j-1] and \
                                (s[i-1] == p[j-1] or p[j-1] == "?")

        return mem[-1][-1]

由于这个问题比较简单,所以我们可以直接从正面去解决它。我们先遍历sp

s: a d c e b 
   i
p: * a * b
   j

我们发现j指向的元素是*,所以我们记录下*的位置和此时i的位置,然后我们j++判断下一个位置。

s: a d c e b 
   i
p: * a * b
     j
star:0
i_index:0

接着我们判断ij所指向的元素是不是相同,如果是的话我们i++;j++

s: a d c e b 
     i
p: * a * b
       j
star:0
i_index:0

此时j所指向的元素又是*,我们按之前那样操作。

s: a d c e b 
       i
p: * a * b
         j
star:2
i_index:1

此时,我们发现j既没有指向*ij所指向的元素又不相等。我们要回过头来看*,我们此时是知道*的位置的,所以我们直接i++即可,也就是*此时匹配两次。

s: a d c e b 
         i
p: * a * b
         j
star:2
i_index:1

同上,我们再令i++

s: a d c e b 
           i
p: * a * b
         j
star:2
i_index:1

此时,我们发现ij所指向的元素相同,所以我们i++;j++,此时我们发现匹配结束了,并且匹配成功。

我们回顾一下上面的整个过程,这其中还有一些漏洞,首先如果上述三种情况都不存在,那么我们直接返回false。如果我们匹配结束后(s匹配完),我们发现j所指向的元素以及其后的所有元素是*以外的元素话,我们返回false

class Solution:
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        s_len, p_len = len(s), len(p)
        i, j, star, i_index = 0, 0, -1, 0
        while i < s_len:
            if j < p_len and (p[j] == '?' or p[j] == s[i]):
                i += 1
                j += 1
            elif j < p_len and p[j] == '*':
                star = j
                j += 1
                i_index = i
            elif star != -1:
                j = star + 1
                i_index += 1
                i = i_index
            else:
                return False
        
        while j < p_len and p[j] == '*':
            j += 1

        return j == p_len

reference:

https://leetcode.com/problems/wildcard-matching/discuss/17810/Linear-runtime-and-constant-space-solution

我将该问题的其他语言版本添加到了我的GitHub Leetcode

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

展开阅读全文

没有更多推荐了,返回首页