44. 通配符匹配 - 力扣(LeetCode)
给你一个输入字符串 (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
仅由小写英文字母、'?'
或'*'
组成
题解
题目描述:给你两个字符串s
文本字符串和p
模式字符串,其中p
包含两种类型的通配符:
*
:可以匹配任意字符序列(包括空序列)?
:可以匹配任意单个字符
题目要求你检查s
和p
是否匹配。
Example 1:
s = "adceb"
p = "*a*b"
- Output:
true
- Explanation: The first
*
can match an empty sequence, and the second*
matches “dce”.
Example 2:
s = "acdcb"
p = "a*c?b"
- Output:
false
- Explanation: There is no way to match
s
withp
, as the character before the last ins
is ‘c’ but ‘b’ inp
.
这个题用贪心的思路该如何去想呢,在每一步匹配中,我们可以对如何匹配字符串做出局部最优选择,如*
,可以表示任何字符序列,包括空序列,可以在匹配字符串的过程中及时调整匹配。
- 对于
?
:由于?
只匹配一个字符,即自动选择s
的当前字符与p
中的?
匹配。 - 对于
*
:因为他可以匹配任意字符序列,所以按照贪心的思路,首先用空序列匹配*
,然后,如果需要,根据模式和字符串其余部分扩展或缩小匹配的字符数量。
贪心算法的实现思路
- 两个指针+回溯:首先定义两个指针,分别是
s
(sIdx
)和p
(pIdx
),进行模式匹配,如果出现不匹配,则回溯到p
中*
的最后一个位置(如果有),并尝试不同的匹配。 ?
通配符处理:在迭代中,如果s
和p
中的当前字符匹配,或者p[pIdx]
是?
,只需将两个指针向前移动。- 处理
*
通配符:如果p
是*
,需要分别标记*
在p
中的位置(用starIdx
),和在s
中的对应位置(sTmpIdx
表示)。表示*
匹配s
中的空字符的起点。如果稍后出现不匹配,则回溯到starIdx
并增加sTmpIdx
,表示*
现在应该在s
中多匹配一个字符。将pIdx
重置为starIdx+1
,将sIdx
重置为sTmpIdx
,继续匹配。 - 最后检查:遍历
s
后,确保p
中所有剩余的字符都是*
。如果是,它们可以匹配空序列,因此,模式匹配字符串。
具体示例:
Consider :s = "adceb"
and p = "*a*b"
Initial Setup
s = "adceb"
p = "*a*b"
sIdx = 0
,pIdx = 0
,starIdx = -1
,sTmpIdx = -1
Iteration 1
p[0]
is*
, so we updatestarIdx = 0
andsTmpIdx = 0
.- Increment
pIdx
to 1. sIdx
remains 0.
Iteration 2
p[1]
is'a'
, buts[0]
is'a'
. This is a mismatch.- Since
starIdx != -1
, we use the star to match one more character. - Increment
sTmpIdx
to 1. SosTmpIdx = 1
. - Update
pIdx = starIdx + 1
, which meanspIdx = 1
. - Update
sIdx = sTmpIdx
, which meanssIdx = 1
.
Iteration 3
- Now
p[1]
is'a'
ands[1]
is also'a'
. This is a match. - Increment both
sIdx
andpIdx
by 1. sIdx = 2
,pIdx = 2
.
Iteration 4
p[2]
is'*'
. UpdatestarIdx = 2
andsTmpIdx = 2
.- Increment
pIdx
to 3. sIdx
remains 2.
Iteration 5
p[3]
is'b'
, buts[2]
is'd'
. This is a mismatch.- Since
starIdx != -1
, we use the star to match one more character. - Increment
sTmpIdx
to 3. SosTmpIdx = 3
. - Update
pIdx = starIdx + 1
, which meanspIdx = 3
. - Update
sIdx = sTmpIdx
, which meanssIdx = 3
.
Iteration 6
p[3]
is'b'
ands[3]
is'c'
. This is a mismatch.- We repeat the backtracking process:
- Increment
sTmpIdx
to 4. SosTmpIdx = 4
. - Update
pIdx = starIdx + 1
, which meanspIdx = 3
. - Update
sIdx = sTmpIdx
, which meanssIdx = 4
.
Iteration 7
p[3]
is'b'
ands[4]
is'b'
. This is a match.- Increment both
sIdx
andpIdx
by 1. sIdx = 5
,pIdx = 4
.
Final Check
sIdx
is now equal tos.size()
, so we exit the while loop.- Check remaining characters in
p
. SincepIdx
is also at the end ofp
, there are no more characters to check.
class Solution {
public:
bool isMatch(string s, string p) {
int sIdx = 0, pIdx = 0; // Pointers for s and p.
int starIdx = -1, sTmpIdx = -1; // Indices to track the most recent '*' position in p and the corresponding position in s.
while (sIdx < s.size()) {
// If the characters match or pattern has '?', move both pointers.
if (pIdx < p.size() && (p[pIdx] == s[sIdx] || p[pIdx] == '?')) {
++sIdx;
++pIdx;
}
// If pattern has '*', record the position and assume it matches zero characters initially.
else if (pIdx < p.size() && p[pIdx] == '*') {
starIdx = pIdx;
sTmpIdx = sIdx;
++pIdx;
}
// If a mismatch occurs and there was a previous '*', backtrack.
// Try to match '*' with one more character in s.
else if (starIdx != -1) {
pIdx = starIdx + 1;
sIdx = ++sTmpIdx;
}
// If no '*' to backtrack to, return false.
else {
return false;
}
}
// Check if the remaining characters in pattern are all '*'.
// They can match the empty sequence at the end of s.
for (; pIdx < p.size(); ++pIdx) {
if (p[pIdx] != '*') {
return false;
}
}
// If we've processed both strings completely, it's a match.
return true;
}
};