题目链接:https://leetcode.com/problems/wildcard-matching/
题目:
'?'
and '*'
.
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
思路:
看到tag是backtracking,dp,先用回溯试着做一下,首先思考如何把匹配问题转为为回溯问题,可以将模式串看成一棵类似于“子集树”,即对*号可以选择使用这个*匹配待匹配串某字符,也可选择不使用,使用完这个*匹配某字符后又可以将*留给下一个字符做匹配,做出来应该能过所有case,不过有几个case时间在秒级别,TLE了。 而且代码越写到后面越像dp。
遂考虑dp,dp思路就简单了,二维数组dp[i][j]表示两个分别为0~i(待匹配串),0~j(模式串)的串是否能匹配,状态转移方程:
1.当s[i]==p[j] 或者p[j]=='?': dp[i][j]=dp[i-1][j-1]
2.当p[j]=='*':dp[i][j]= 如果dp[k][j-1] k<=i,有为true的,即待匹配串是startwith(p[:j-1])的,那么待匹配串剩余部分可以由*匹配。
3.其他情况dp[i][j]默认为false
这个时间复杂度是O(n^3)的,AC之后只能超过1.08%的解法。 遂考虑优化,我们发现上面第2种情况是需要遍历才能知道p[:j-1]是否是startwiths[:i]的,这个遍历是可以省略的,用一个flag表示当前模式串p[:j-1]是否startwith s[:i],首先将遍历过程改为 【先让某个模式字符对所有带匹配串进行匹配】也就是将对P的for遍历放在前面,这样如果 ab能startwith abc则ab也一定startwith abcd,这样就少了一层遍历,时间复杂度降为O(n^2),不过AC之后也只能超过5%的解法。。。。泪
算法1:
public boolean isMatch(String s, String p) {
dfs(s, 0, p, 0);
return flag;
}
boolean flag = false;
public void dfs(String s, int i, String p, int j) {
if (flag) // 已经有一条路径可以匹配 则返回
return;
if (i == s.length()) {
while (j < p.length()) {// 待匹配串已经匹配完,模式串如果剩余部分全为* 则返回true
if (p.charAt(j) != '*') {
return;
}
j++;
}
flag = true;
return;
}
if (j == p.length())
return;
if (p.charAt(j) == '?' || p.charAt(j) == s.charAt(i)) {
dfs(s, i + 1, p, j + 1);
} else if (p.charAt(j) == '*') {// 如果模式串是*
// 选择*作为s的匹配
dfs(s, i + 1, p, j);
dfs(s, i + 1, p, j + 1);
// 不选择*
dfs(s, i, p, j + 1);
}
}
算法2:
public boolean isMatch(String s, String p) {
boolean dp[][] = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
for (int i = 0; i <= s.length(); i++) {
for (int j = 1; j <= p.length(); j++) {
char pc = p.charAt(j - 1);
if (i > 0 && (pc == '?' || pc == s.charAt(i - 1))) {
dp[i][j] = dp[i - 1][j - 1];
} else if (pc == '*') {
for (int k = i; k >= 0; k--) {// 0~j-1的模式串是否能startwith匹配字符串0~i部分
if (dp[k][j - 1]) {
dp[i][j] = true;
break;
}
}
}
}
}
return dp[s.length()][p.length()];
}
算法3:
public boolean isMatch(String s, String p) {
boolean dp[][] = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
for (int j = 1; j <= p.length(); j++) {
char pc = p.charAt(j - 1);
boolean flag = false;
for (int i = 0; i <= s.length(); i++) {
flag = flag||dp[i][j-1];
if (i > 0 && (pc == '?' || pc == s.charAt(i - 1))) {
dp[i][j] = dp[i - 1][j - 1];
} else if (pc == '*') {
dp[i][j] = flag;
}
}
}
return dp[s.length()][p.length()];
}