题目描述
给你一个输入字符串 (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
仅由小写英文字母、'?'
或'*'
组成
解题方法
dfs + 动态规划
这道题一开始只用了dfs,结果超时了,然后把动态规划加上存储dfs状态,然后就过了。
先说一下dfs思路。我们将p
和s
从头开始遍历,p
的遍历位置为pi
,s
的遍历位置为si
。dfs对应下面三种情况进行不同处理。
- 当
pi
位置字符是英文字母时,需要检查si
位置的字符是否相同。相同则pi
和si
往后移动一位,继续dfs遍历;不同则当前dfs失败。 - 当
pi
位置字符是'?'
时,pi
和si
往后移动一位,继续dfs遍历。 - 当
pi
位置字符是'*'
时,需要进行一个for循环的dfs遍历:pi
往后移动一位,si
先不移动,进行后续dfs遍历,若后续dfs遍历成功,则当前dfs成功;否则,si
往后移动一位,进行后续dfs遍历,若后续dfs遍历成功,则当前dfs成功;否则,si
一直往后移动,直到后续dfs遍历成功;若si
移动到末尾也没成功,则当前dfs失败。
然后,我们用dp
数组存储dfs对应si
和pi
位置的状态(即s以si为起点,p以pi为起点是否完全匹配)。进行dfs时,若该位置已经被dp
记录过,就不需要进行后续dfs遍历了。
java代码
public boolean isMatch(String s, String p) {
p = removeDuplicateCharacter(p);
// 代表字符串s以si为起点,p以pi为起点是否完全匹配,0代表未知,1代表不匹配,2代表匹配
int[][] dp = new int[s.length() + 1][p.length() + 1];
// si和pi都匹配到字符串末尾
dp[s.length()][p.length()] = 2;
return dfs(s, p, 0, 0, dp) == 2;
}
// 去除重复的'*'字符
public String removeDuplicateCharacter(String p) {
StringBuilder result = new StringBuilder();
// 出现'*'
boolean flag = false;
for(int i = 0; i < p.length(); i++) {
char c = p.charAt(i);
if (!flag) {
result.append(c);
} else {
if (c != '*') {
result.append(c);
flag = false;
}
}
if (c == '*' && !flag) {
flag = true;
}
}
return result.toString();
}
// 字符串s以si为起点,p以pi为起点是否完全匹配,返回结果1代表不匹配,2代表匹配
public int dfs(String s, String p, int si, int pi, int[][] dp) {
if(dp[si][pi] != 0) {
return dp[si][pi];
}
// si没匹配到末尾,pi匹配到末尾
if (si != s.length() && pi == p.length()) {
dp[si][pi] = 1;
return 1;
}
// si匹配到末尾,pi没匹配到末尾
if (si == s.length() && pi != p.length()) {
if(p.charAt(pi) != '*') {
dp[si][pi] = 1;
return 1;
}
dp[si][pi] = dfs(s, p, si, pi + 1, dp);
return dp[si][pi];
}
// '?'代表s和p匹配一个字符
if (p.charAt(pi) == '?') {
dp[si][pi] = dfs(s, p, si + 1, pi + 1, dp);
return dp[si][pi];
}
if (p.charAt(pi) == '*') {
// '*'从匹配0个字符开始,若后续匹配成功则返回true。否则,'*'匹配1个字符,失败再匹配2个字符,直到匹配到s末尾。
// s匹配到末尾还是匹配失败,则以si和pi为起点的匹配失败
for(int i = si; i <= s.length(); i++) {
int result = dfs(s, p, i, pi + 1, dp);
if (result == 2) {
dp[si][pi] = 2;
return 2;
}
}
dp[si][pi] = 1;
return 1;
}
// si和pi字符相同,继续往后匹配
if(s.charAt(si) == p.charAt(pi)) {
dp[si][pi] = dfs(s, p, si + 1, pi + 1, dp);
return dp[si][pi];
}
// pi不是特殊字符,且si和pi字符不同,则匹配失败
dp[si][pi] = 1;
return 1;
}
复杂度分析
时间复杂度:
O
(
m
n
)
O(mn)
O(mn),
m
m
m和
n
n
n分别为s
和p
的长度,最坏情况下需要进行
m
×
n
m \times n
m×n次的dfs遍历。
空间复杂度:
O
(
m
n
)
O(mn)
O(mn),需要
m
×
n
m \times n
m×n的空间存储二位数组。
相似题目
- 个人公众号
- 个人小游戏