题目
题目描述
给你一个输入字符串 (s) 和一个字符模式 § ,请你实现一个支持 ‘?’ 和 ‘’ 匹配规则的通配符匹配:
‘?’ 可以匹配任何单个字符。
'’ 可以匹配任意字符序列(包括空字符序列)。
判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。
示例 1:
输入:s = “aa”, p = “a”
输出:false
解释:“a” 无法匹配 “aa” 整个字符串。
示例 2:
输入:s = “aa”, p = ""
输出:true
解释:'’ 可以匹配任意字符串。
示例 3:
输入:s = “cb”, p = “?a”
输出:false
解释:‘?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。
提示:
0 <= s.length, p.length <= 2000
s 仅由小写英文字母组成
p 仅由小写英文字母、‘?’ 或 ‘*’ 组成
题解
通配符匹配问题可以通过动态规划(Dynamic Programming, DP)来高效解决。这个问题要求我们支持两种特殊字符:?
和 *
,其中 ?
可以匹配任何单个字符,而 *
可以匹配任意字符序列(包括空字符序列)。我们需要构建一个二维的DP表,用来记录子串之间的匹配状态。
动态规划解法
状态定义
- 设
dp[i][j]
表示字符串s
的前i
个字符和模式p
的前j
个字符是否匹配。
初始化
dp[0][0] = True
,表示空字符串与空模式是匹配的。- 如果模式
p
开头有*
,则dp[0][j]
(对于所有j > 0
)也可以为True
,因为*
可以匹配空字符序列。
状态转移方程
- 当
p[j-1] == '?'
或者p[j-1]
是一个小写字母并且等于s[i-1]
:dp[i][j] = dp[i-1][j-1]
- 当
p[j-1] == '*'
:*
可以匹配空字符序列:dp[i][j] = dp[i][j-1]
*
可以匹配至少一个字符:dp[i][j] = dp[i-1][j]
结果
- 最终的结果保存在
dp[m][n]
中,其中m
和n
分别是字符串s
和模式p
的长度。
Python 实现代码
以下是按照上述思路实现的 Python 代码:
def isMatch(s: str, p: str) -> bool:
m, n = len(s), len(p)
# 创建DP表,并初始化
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
# 初始化第一行,处理模式中开头的'*'
for j in range(1, n + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 1]
# 填充DP表
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j - 1] == '?' or p[j - 1] == s[i - 1]:
dp[i][j] = dp[i - 1][j - 1]
elif p[j - 1] == '*':
dp[i][j] = dp[i][j - 1] or dp[i - 1][j]
return dp[m][n]
代码解释
-
初始化:首先创建一个
(m+1) x (n+1)
大小的DP表,并将dp[0][0]
设置为True
。然后检查模式p
是否以*
开始,如果是,则相应位置也设置为True
。 -
填充DP表:使用双重循环遍历字符串
s
和模式p
的每一个字符。根据当前字符的不同情况(普通字符、?
或*
),更新DP表中的值。 -
结果:最终返回
dp[m][n]
的值,即整个字符串s
和模式p
是否完全匹配。
这种方法确保了我们可以有效地处理包含 ?
和 *
的通配符匹配问题,并且能够应对题目中给出的最大输入规模(字符串和模式长度均不超过2000)。