44.Wildcard Matching¶
Tags: Hard
Greedy
String
Backtracking
Dynamic Programming
Links: https://leetcode.com/problems/wildcard-matching/
Given an input string (s
) and a pattern (p
), implement wildcard pattern matching with support for '?'
and '*'
.
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).
The matching should cover the entire input string (not partial).
Note:
s
could be empty and contains only lowercase lettersa-z
.p
could be empty and contains only lowercase lettersa-z
, and characters like?
or*
.
Example 1:
Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Example 2:
Input:
s = "aa"
p = "*"
Output: true
Explanation: '*' matches any sequence.
Example 3:
Input:
s = "cb"
p = "?a"
Output: false
Explanation: '?' matches 'c', but the second letter is 'a', which does not match 'b'.
Example 4:
Input:
s = "adceb"
p = "*a*b"
Output: true
Explanation: The first '*' matches the empty sequence, while the second '*' matches the substring "dce".
Example 5:
Input:
s = "acdcb"
p = "a*c?b"
Output: false
class Solution {
public:
bool isMatch(string s, string p) {
std::ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
//iStar记录s与*匹配的位置,jStar记录p中*的位置
int i = 0, j = 0, iStar = -1, jStar = -1, m = s.size(), n = p.size();
while (i < m) {
if (j < n && (s[i] == p[j] || p[j] == '?')) {
++i; ++j;
}
else if (j < n && p[j] == '*') { //*什么都不匹配
iStar = i;
jStar = j++;
}
else if (iStar >= 0) { //*至少匹配一个字符
i = ++iStar;
j = jStar + 1;
}
else return false;
}
//可能存在p中的*把s都匹配了,但是p中还剩余不是*的字符未匹配
while (j < n && p[j] == '*') ++j;
return j == n;
}
};
时间复杂度O(n),贪心的策略。用iStar
记录s
与*
匹配的位置, jStar
记录p
中*
的位置,初始化为-1来进行标识。
首先如果s[i] == p[j] || p[j] == '?'
,意味着可以完成正常一对一匹配,所以i
和j
的位置增加即可。
如果遇到了p[j] == '*'
的情况,那么就要区别对待,因为*
可以匹配0个或者多个字符。如果*
什么都不匹配,用iStar
和jStar
记录下遇到*
的各自的位置,然后让j
的位置增加看能否继续匹配。另外一种情况是*
至少匹配一个字符,那么让iStar
增加一个单位,意味着在字符串s
中至少有一个字符被匹配,于是去检验下一个能否被匹配,其实相当于一个回退的功能。
举例说明:设s = aab, p = a*c
,这里假如s
的第二个a
和p
的*
匹配,那么iStar = 1, jStar = 1
,发现b
和c
不匹配,于是iStar = 2
意味着*
匹配了两个字符,这样就到了末尾。
最后检查字符串p
还是没有到末尾,所以无法完成匹配。
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
vector<vector<bool>> d(m + 1, vector<bool>(n + 1, false));
d[0][0] = true; //s和p都为空,完成匹配
//预处理s为空,p为连续*的情况
for (int i = 1; i <= n; ++i) {
if (p[i - 1] == '*')
d[0][i] = true;
else
break;
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (s[i - 1] == p[j - 1] || p[j - 1] == '?')
d[i][j] = d[i - 1][j - 1];
else if (p[j - 1] == '*')
d[i][j] = d[i - 1][j] | d[i][j - 1];
}
}
return d[m][n];
}
};
还可以从动态规划的角度去求解问题,为了求解问题方便,下标从1开始计数(也就是1对应着字符串中的0的位置)。用d[i][j]
表示字符串s
的前i
个字符和字符串p
的前j
个字符是否完成匹配。然后去寻找状态转移方程。
假如在在字符串的第i
个位置和字符串p
的第j
个位置相同或j
的位置为?
,那么只需要去考察d[i - 1][j - 1]
。
如果在p
中的位置j
处是*
,那么*
的作用可以匹配,也可以什么都不匹配。假如什么都不匹配,则依赖于d[i][j - 1]
,当前完成匹配,还可以继续匹配多个,那么依赖于d[i - 1][j]
。
完成初始化工作,如果初始两个字符串都为空,那么肯定可以完成匹配。另外考虑字符串s
初始为空,而字符串p
有前导的*
,比如*****aa
,那么这些前导的*
的部分其实可以完成匹配,也就是*
匹配0个字符,所以需要对这个部分完成初始化。
时间复杂度是O(mn)。