Leetcode1-100: 44. Wildcard Matching
问题描述
题目要求: 实现text和pattern的匹配功能,输出true和false
解题思路
这一题跟第10题(Regular Expression Matching)很像,但是加上了'?'
,去掉了'.'
跟那一题很像,不过用backtrack的话一直通不过说超时,暂时只用了dp和迭代来做。其中dp可以优化一下,具体内容见代码。
implement 1
使用dp来做,dp[i][j]代表s.substring(i)和p.substring(j)的匹配结果,那么每次迭代会出现三个情况:
1. i和j代表的元素匹配上,或者pattern中j对应的字符是'?'
的话直接匹配即可
例如: s = “abcde” p = “abc?e”,那么dp[i][j] = dp[i-1][j-1]
2. 没有匹配上但是pattern中遇到了*
例如: s = “abcde” p = “abc*e”,
第四个元素的位置,可以有两种情况,
一种是"*"
匹配了空串,这样对应的结果就是dp[i][j] = dp[i-1][j]
另一种是"*"
匹配了任意串,这样对应的结果就是dp[i][j] = dp[i][j-1]
3. 没有匹配上并且没有任何特殊符号
直接dp[i][j] = false即可
implement 2
对于dp算法,可以优化一下空间使用,因为每次更新dp[i][j]的时候只需要用到dp[i-1][j-1],dp[i][j-1],dp[i-1][j]三个数,所以可以优化一下空间使用,不再使用dp[s.length()][p.length()]而是用dp[2][p.length()]即可。
implement 3
直接迭代即可,用两个指针指着s和p的对应位置,每次读取指向的字符:
1. 指针对应的位置匹配上或者pattern中出现了"?",这样直接s和p指针都往后一个位置即可
2. 不匹配并且没有读取到的时候p回到star前面的位置并且匹配一个字符,即ss++,同时更新track的值
3. 当读取到一个的时候,记录star,track的位置,然后更新pp表示*匹配一个空串
4. 如果上述情况都没出现,直接返回false
代码实现
implement 1
public boolean isMatch(String s, String p) {
//1. dynamic programming
//dp[i][j]: isMatch(s.substring(0, i+1), p.substring(0, j+1))
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
dp[0][0] = true;
for(int i = 1; i < p.length()+1; i++)
dp[0][i] = dp[0][i-1] && (p.charAt(i-1) == '*');
for(int i = 1; i < s.length(); i++)
dp[i][0] = false;
for(int i = 1; i < s.length()+1; i++)
for(int j = 1; j < p.length()+1; j++) {
//当i,j对应的字符相同或者j对应的是'?'的时候说明匹配成功,只需要看前面的情况即可
if(s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '?')
dp[i][j] = dp[i-1][j-1];
//当j对应的字符是'*'说明可以匹配任意串或者空串,若是任意则等于dp[i][j-1],若是空则等于dp[i-1][j]
else if(p.charAt(j-1) == '*')
dp[i][j] = dp[i-1][j] || dp[i][j-1];
else
dp[i][j] = false;
}
return dp[s.length()][p.length()];
}
复杂度分析:
时间复杂度: O(n2)
空间复杂度: O(1)
implement 2
public boolean isMatch(String s, String p) {
// 2. dynamic programming 空间优化: 由于计算dp[i][j]只用dp[i-1][j-1], dp[i][j-1], dp[i-1][j], 所以只需要存储两行元素即可完成dp(即当前行和上一行)
boolean[][] dp = new boolean[2][p.length()+1];
dp[0][0] = true;
for(int i = 1; i < p.length()+1; i++)
dp[0][i] = dp[0][i-1] && (p.charAt(i-1) == '*');
for(int i = 1; i < s.length()+1; i++) {
dp[i%2][0] = false;
for(int j = 1; j < p.length()+1; j++) {
//当i,j对应的字符相同或者j对应的是'?'的时候说明匹配成功,只需要看前面的情况即可
if(s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '?')
dp[i%2][j] = dp[(i-1)%2][j-1];
//当j对应的字符是'*'说明可以匹配任意串或者空串,若是任意则等于dp[i][j-1],若是空则等于dp[i-1][j]
else if(p.charAt(j-1) == '*')
dp[i%2][j] = dp[(i-1)%2][j] || dp[i%2][j-1];
else
dp[i%2][j] = false;
}
}
return dp[s.length()%2][p.length()];
}
可以看出来,空间效率高了很多
复杂度分析:
时间复杂度: O(n2)
空间复杂度: O(1)
implement 3
public boolean isMatch(String s, String p) {
//3. 迭代,并用两个指针指向两个串要匹配的元素
int ss = 0, pp = 0;
int star = -1, track = 0;
while(ss < s.length()) {
if(pp < p.length() && (s.charAt(ss) == p.charAt(pp)||p.charAt(pp) == '?')) {
ss++;
pp++;
} else if(pp < p.length() && p.charAt(pp) == '*') {
//当读取到一个*的时候,记录star,track的位置,然后更新pp表示*匹配一个空串
star = pp;
track = ss;
pp++;
} else if(star != -1) {
//不匹配并且没有读取到*的时候p回到star前面的位置并且匹配一个字符,即ss++,同时更新track的值
track++;
ss = track;
pp = star+1;
}
else
return false;
}
while(pp < p.length() && p.charAt(pp) == '*')
pp++;
return pp == p.length();
}
复杂度分析:
时间复杂度: O(mn) ,m,n代表s和p的长度
空间复杂度: O(1)