题目:https://leetcode-cn.com/problems/wildcard-matching/
双指针法的解题思路参考力扣上的代码范例
代码运行结果:
执行用时 :3 ms, 在所有 Java 提交中击败了99.76%的用户
内存消耗 :40.1 MB, 在所有 Java 提交中击败了50.00%的用户
使用动态规划时,解题思路和 10. 正则表达式匹配 非常相似,代码如下:
class Solution {
public boolean isMatch(String s, String p) {
int sl = s.length(), pl = p.length();
//空的pattern只能匹配空字符串
if(pl == 0)
return sl == 0;
//dp[i][j]表示p的前j个字符能否匹配s的前i个字符
boolean[][] dp = new boolean[sl + 1][pl + 1];
//空的pattern可以匹配空字符串
dp[0][0] = true;
//空的pattern不能匹配非空字符串
for(int i = 1; i <= sl; i++)
dp[i][0] = false;
//pattern为"*"时可以匹配空字符串
dp[0][1] = p.charAt(0) == '*';
//当pattern长度大于1时,只有当pattern是"****"形式的才可以匹配空字符串
for(int j = 2; j <= pl; j++)
dp[0][j] = p.charAt(j - 1) == '*' && dp[0][j - 1];
//开始动态规划
char sc, pc;
for(int i = 1; i <= sl; i++){
sc = s.charAt(i - 1);
for(int j = 1; j <= pl; j++){
pc = p.charAt(j - 1);
//如果pc不是'*',说明pc代表了某一个具体的字符
//此时需要sc和pc匹配且dp[i - 1][j - 1]为true时dp[i][j]才为true
if(pc != '*')
dp[i][j] = isMatchChar(sc, pc) && dp[i - 1][j - 1];
//如果pc是'*',那么pc可以代表任意长的字符串,我们假设此时p前j个字符为"abc*"
//1. 假设'*'匹配的是空字符串,那么只有当dp[i][j - 1]为true时dp[i][j]才是true
// 举例:如果s前i个字符为"abd",那么p前j - 1个字符"abc"不能匹配s前i个字符"abd"
// 那么"abc*"肯定也不能匹配"abd"
//2. 假设'*'匹配的是任一字符,那么只有当dp[i - 1][j - 1]为true时dp[i][j]才是true
// 举例:如果s前i个字符为"abcd",那么p前j - 1个字符"abc"能匹配s前i - 1个字符"abc"
// 那么"abc*"肯定也能匹配"abcd"
//3. 假设'*'匹配的是任两字符,那么只有当dp[i - 2][j - 1]为true时dp[i][j]才是true
// 举例:如果s前i个字符为"abddd",那么p前j - 1个字符"abc"不能匹配s前i - 2个字符"abd"
// 那么"abc*"肯定也不能匹配"abddd"
//综上,dp[0][j - 1]到dp[i][j - 1]中只要有一个为true,那么dp[i][j]就为true
else
dp[i][j] = existTrue(dp, i, j - 1);
}
}
return dp[sl][pl];
}
private boolean isMatchChar(char s, char p){
return p == '?' || s == p;
}
//查看dp[0:i][j]中是否存在至少一个为true
private boolean existTrue(boolean[][] dp, int i, int j){
for(int x = i; x >= 0; x--)
if(dp[x][j])
return true;
return false;
}
}
上面的方式由于每次循环都可能需要调用existTrue()方法,时间复杂度是很高的(执行用时110ms左右)
因此再定义一个数组来保存中间量,避免重复计算,代码如下(改进后执行用时32ms左右):
class Solution {
public boolean isMatch(String s, String p) {
int sl = s.length(), pl = p.length();
//空的pattern只能匹配空字符串
if(pl == 0)
return sl == 0;
//dp[i][j]表示p的前j个字符能否匹配s的前i个字符
boolean[][] dp = new boolean[sl + 1][pl + 1];
//colExistTrue[j]表示第j列是否已经出现了true
boolean[] colExistTrue = new boolean[pl + 1];
//空的pattern可以匹配空字符串
dp[0][0] = true;
//第0列已经出现了true
colExistTrue[0] = true;
//空的pattern不能匹配非空字符串
for(int i = 1; i <= sl; i++)
dp[i][0] = false;
//pattern为"*"时可以匹配空字符串,如果可以匹配,那么第一列出现了true
colExistTrue[1] = dp[0][1] = p.charAt(0) == '*';
//当pattern长度大于1时,只有当pattern是"***"形式的才可以匹配空字符串,并实时更新colExistTrue
for(int j = 2; j <= pl; j++)
colExistTrue[j] = dp[0][j] = p.charAt(j - 1) == '*' && dp[0][j - 1];
char sc, pc;
for(int i = 1; i <= sl; i++){
sc = s.charAt(i - 1);
for(int j = 1; j <= pl; j++){
pc = p.charAt(j - 1);
if(pc != '*')
dp[i][j] = isMatchChar(sc, pc) && dp[i - 1][j - 1];
else
dp[i][j] = colExistTrue[j - 1];
//dp[i][j]确定后,如果colExistTrue[j]为false,那么colExistTrue[j]有可能更新为true
if(!colExistTrue[j])
colExistTrue[j] = dp[i][j];
}
}
return dp[sl][pl];
}
private boolean isMatchChar(char s, char p){
return p == '?' || s == p;
}
}
主要思路是定义 pIdx 和 sIdx 两个指针分别指向 p 和 s 的开头
如果 pIdx 指向的不是 '*' 且两个指针指向的字符能匹配,则两个指针都右移一格
如果 pIdx 指向了 '*' ,那么先假设星号匹配的是空字符串,如果后续没有出现不匹配,说明假设成立;否则继续假设星号匹配任一个字符;再有不匹配则假设星号匹配任意两个字符;以此类推
class Solution {
public boolean isMatch(String s, String p) {
int sLen = s.length(), pLen = p.length();
//定义两个指针,sIdx指向s的开头,pIdx指向p的开头
int sIdx = 0, pIdx = 0;
//这两个值分别用于记录pIdx和sIdx,便于回溯
int starIdx = -1, sTmpIdx = -1;
while (sIdx < sLen) {
//如果pIdx指向的字符不是'*'且能和sIdx指向的字符匹配,则两者都向右移动
if (pIdx < pLen && (p.charAt(pIdx) == '?' || p.charAt(pIdx) == s.charAt(sIdx))) {
++sIdx;
++pIdx;
//如果pIdx指向了'*',则假设该星号匹配的是空字符串,如果后面没出现问题,说明假设成立
//此时记录当前的pIdx和sIdx,且只有pIdx右移
} else if (pIdx < pLen && p.charAt(pIdx) == '*') {
starIdx = pIdx;
sTmpIdx = sIdx;
++pIdx;
//==================如果不是以上两种情况,说明出现了不匹配现象==================
//如果starIdx为-1,说明之前根本没遍历过'*',此时出现了不匹配,只能说明p和s不匹配
} else if (starIdx == -1) {
return false;
//如果starIdx不是-1,且出现了不匹配,说明之前假设(星号匹配空字符串)有误
//因此我们需要假设星号匹配任意一个字符,如果后面没出现问题,说明假设成立
//因此pIdx回到星号后面的字符,同时sIdx回到之前记录位置的后一个位置,并将该位置记录下来
//若之后发现匹配一个字符也不行,则继续执行这段代码,此时假设星号匹配任意两个字符,以此类推
} else {
pIdx = starIdx + 1;
sIdx = sTmpIdx + 1;
sTmpIdx = sIdx;
}
}
//如果s已经遍历完了,但p还有剩余,那么剩余部分必须都是星号才说明匹配了
for (int i = pIdx; i < pLen; i++)
if (p.charAt(i) != '*')
return false;
return true;
}
}