今天我们看一道 leetcode hard 难度题目:通配符匹配。
题目
给你一个输入字符串 (s
) 和一个字符模式 (p
) ,请你实现一个支持 '?'
和 '*'
匹配规则的通配符匹配:
'?'
可以匹配任何单个字符。'*'
可以匹配任意字符序列(包括空字符序列)。 判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。
示例 1:
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
思考
最直观的思考是模拟匹配过程,以 s = "abc", p = "abd" 为例,匹配过程是这样的:
"a" 匹配 "a",通过
"b" 匹配 "b",通过
"c" 不匹配 "d",失败
只要匹配过程有任何一个字符匹配失败,则整体匹配失败。如果没有 '?'
与 '*'
号,题目则异常简单,只要一个指针按顺序扫描,扫描过程每个字符必须相等,且同时结束才算成功,否则判断失败。
加上 '?'
依然很简单,因为 '?'
号一定会消耗掉,只是它可以匹配任何字符,所以还是一个指针扫描,遇到 p
中 '?'
号时,跳过判等继续向后扫描即可。
加上 '*'
号时该题成为 hard 的第一个原因。由于 '*'
可以匹配空字符,也可以匹配任意多个字符,所以遇到 p
中 '*'
时有三种处理可能性:
当做没见过
'*'
,直接判等,不消耗s
,并匹配p
的下一个字符。此时对应'p'
不匹配任何字符。直接消耗掉
'*'
判等,同时消耗s
与p
。此时'*'
与'?'
的作用等价。不消耗
'*'
,但是消耗s
。此时对应'*'
匹配多个字符而可以不消耗自己的特性。
很容易想到写一个递归的实现,代码如下:
function isMatch(s: string, p: string): boolean {
return myIsMatch(s.split(''), p.split(''))
};
function myIsMatch(sArr: string[], pArr: string[]): boolean {
// 如果 s p 都匹配完了,或 p 还剩任意数量的 *,都算匹配通过
if (
(sArr.length === 0 && pArr.length === 0) ||
(sArr.length === 0 && pArr.every(char => char === '*'))
) {
return true
}
// 如果任意一项长度为 0,另一项不为 0,则匹配失败
if (
(sArr.length === 0 &