给定一个字符串 (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 , j − 1 ) o r f ( i − 1 , j ) i f p [ j − 1 ] = ′ ∗ ′ f(i,j)=f(i,j-1)\ or\ f(i-1, j)\ \ if\ \ p[j-1]='*' f(i,j)=f(i,j−1) or f(i−1,j) if p[j−1]=′∗′
- f ( i , j ) = f ( i − 1 , j − 1 ) a n d ( s [ i − 1 ] = = p [ j − 1 ] ∣ ∣ p [ j − 1 ] = = ′ ? ′ ) i f p [ j − 1 ] ≠ ′ ∗ ′ 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)=f(i−1,j−1) and (s[i−1]==p[j−1]∣∣p[j−1]==′?′) if p[j−1]̸=′∗′
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]
由于这个问题比较简单,所以我们可以直接从正面去解决它。我们先遍历s
和p
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
接着我们判断i
和j
所指向的元素是不是相同,如果是的话我们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
既没有指向*
,i
和j
所指向的元素又不相等。我们要回过头来看*
,我们此时是知道*
的位置的,所以我们直接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
此时,我们发现i
和j
所指向的元素相同,所以我们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:
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!