# [44] 通配符匹配
给你一个输入字符串 (s) 和一个字符模式 (p) ,请你实现一个支持 '?' 和 '*' 匹配规则的通配符匹配:
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符序列(包括空字符序列)。
判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。
示例 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 仅由小写英文字母、'?' 或 '*' 组成
这道题与 [10] 正则表达式匹配
的题目十分相似,可以用同种方式来进行思考。
详细解答思路参考题解中的注释。
function isMatch(s: string, p: string): boolean {
const sLen = s.length,
pLen = p.length;
const dp: boolean[][] = Array(sLen + 1)
.fill(0)
.map(x => Array(pLen + 1).fill(false));
// 当p为空时,s为空时为true,s不为空时为false
dp[0][0] = true;
// 当s为空时,p不为空时,只有当p的前j个字符均为'*'时,dp[i][j]才为true。其他情况均为false
for (let j = 1; j <= pLen; j++) {
if (p[j - 1] === '*') {
dp[0][j] = true;
} else {
break;
}
}
for (let i = 1; i <= sLen; i++) {
for (let j = 1; j <= pLen; j++) {
if (s[i - 1] === p[j - 1] || p[j - 1] === '?') {
dp[i][j] = dp[i - 1][j - 1];
} else if (p[j - 1] === '*') {
// 此处共可能出现以下三种情况:
// '*'匹配0个字符,即dp[i][j] = dp[i][j - 1]。
// '*'匹配1个字符,即dp[i][j] = dp[i - 1][j - 1]。
// '*'匹配多个字符,即dp[i][j] = dp[i - 1][j]。
dp[i][j] = dp[i][j - 1] || dp[i - 1][j - 1] || dp[i - 1][j];
}
// 其他情况下,失去匹配,dp[i][j] = false。
}
}
return dp[sLen][pLen];
}
注意到,实际上 dp[i][j]
仅与 dp[i][j-1]
, dp[i-1][j]
, dp[i-1][j-1]
有关,那么我们很容易的写出空间复杂度为 O(m)
的解法。
注意这里的遍历顺序需要稍作更改,以防止需要的变量被淹没的问题。
function isMatch(s: string, p: string): boolean {
const sLen = s.length,
pLen = p.length;
const dp: boolean[] = Array(sLen + 1).fill(false);
// 当p为空时,s为空时为true,s不为空时为false
dp[0] = true;
// 保存原算法中 dp[i - 1][j - 1] 的值,用于下一次循环
let pre: boolean;
for (let j = 1; j <= pLen; j++) {
// 保存原算法中 dp[0][j-1] 的值
pre = dp[0];
// 当s为空时,p不为空时,只有当p的前j个所有的字符均为'*'时,结果才为true。其他情况均为false
dp[0] = p[j - 1] === '*' ? dp[0] : false;
for (let i = 1; i <= sLen; i++) {
// 记录下来更改前的值,等价于 dp[i][j-1]
const temp = dp[i];
if (s[i - 1] === p[j - 1] || p[j - 1] === '?') {
// 对应原来的 dp[i - 1][j - 1]。
dp[i] = pre;
} else if (p[j - 1] === '*') {
// 分别对应原来的dp[i][j - 1]、dp[i - 1][j]、dp[i - 1][j - 1]。
dp[i] = temp || dp[i - 1] || pre;
} else {
// 其他情况下,失去匹配,dp[i][j] = false。
dp[i] = false;
}
// 最后给 pre 更新值,作为下一次循环中 dp[i - 1][j - 1] 的值
pre = temp;
}
}
return dp[sLen];
}