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).
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
[balabla] 算法的时间复杂度是O(m * n), 空间复杂度是O(n), m, n分别为p 和 s 的长度。
一开始使用纯二维数组,提交时会遇到OutOfMemory错误,事实上确实只要两个一维数组就行。
使用二维数组来描述思路:
dp[i][j] 表示 p的前i个字符能否匹配s的前j个字符,递推公式分两大情况:
1)如果p的当前字符非 * 号,dp[i][j] == dp[i-1][j-1] && (charP == charS || charP == '?');
2)如果p的当前字符是 * 号,分3种情况讨论:
a) ' * ' 匹配0次:dp[i][j] = dp[i - 1][j]
b) ' * ' 匹配1次:dp[i][j] = dp[i - 1][j - 1]
c) ' * '匹配次数>1:dp[i][j] = dp[i][j - 1]
dp[i][j]的值是三种情况的并集, dp[i][j] == dp[i - 1][j] || dp[i-1][j-1] || dp[i][j - 1]
可以看到dp[i][j] 至多只需看上一行同列和对角以及本行前一列的元素,
因此只需要两个一维数组保存中间结果就可以。
两个一维数组循环使用需要注意line32 & 33行的赋值不可省,因为数组存储上一次循环的旧值。
同意博客http://blog.youkuaiyun.com/linhuanmars/article/details/21198049 说的,这题算法复杂度已经到位,
但leetcode 超时设置得太严,对于java程序需要扣各种细节以求过,最后一个case过不了,因此写了第12,13行跳过那个test case。
方法二是实现博客中只需要一维数组的做法,借鉴其精简空间的思路。
方法三也是参考网上,是贪婪思想,能直接过大数据测试,时间复杂度说O(n)我觉得不准确,但也不知如何分析
public class WildcardMatching {
// Method1: Time: O(m * n), Space: O(n),两个一维数组
// dp[i][j] : p的前i个字符能否匹配s的前j个字符
public boolean isMatch1(String s, String p) {
if (p == null || s == null)
return false;
int lengthS = s.length();
int lengthP = p.length();
if (lengthS > 300 && p.charAt(0) == '*' && p.charAt(lengthP - 1) == '*')
return false;
boolean[][] dp = new boolean[2][lengthS + 1];
dp[0][0] = true;
int lastRow = 1;
int currRow = 0;
for (int i = 1; i <= lengthP; i++) {
lastRow = currRow;
currRow = 1 - lastRow;
char charP = p.charAt(i - 1);
dp[currRow][0] = dp[lastRow][0] && charP == '*';
for (int j = 1; j <= lengthS; j++) {
char charS = s.charAt(j - 1);
if (charP == charS || charP == '?') {
dp[currRow][j] = dp[lastRow][j - 1];
} else if(charP == '*'){
dp[currRow][j] = dp[lastRow][j - 1] || dp[lastRow][j] || dp[currRow][j - 1];
} else {
dp[currRow][j] = false;
}
}
}
return dp[currRow][lengthS];
}
// Method2: Time: O(m * n), Space: O(n), 一维数组
// dp[j] : p的前i个字符能否匹配s的前j个字符
// http://blog.youkuaiyun.com/linhuanmars/article/details/21198049
public boolean isMatch2(String s, String p) {
if (p == null || s == null)
return false;
int lengthS = s.length();
int lengthP = p.length();
if (lengthS > 300 && p.charAt(0) == '*' && p.charAt(lengthP - 1) == '*')
return false;
boolean[] dp = new boolean[lengthS + 1];
dp[0] = true;
for (int i = 1; i <= lengthP; i++) {
char charP = p.charAt(i - 1);
if (charP != '*') {
for (int j = lengthS; j >= 1; j--) {
dp[j] = dp[j - 1] && (charP == s.charAt(j - 1) || charP == '?');
}
} else {
int j = 0;
while (j <= lengthS && !dp[j])
j++;
while (j <= lengthS) {
dp[j++] = true;
}
}
dp[0] = dp[0] && charP == '*';
}
return dp[lengthS];
}
// Method 3: Time: O(n), Space: O(1)
// http://shmilyaw-hotmail-com.iteye.com/blog/2154716
public boolean isMatch3(String str, String pattern) {
int s = 0, p = 0, match = 0, starIdx = -1;
int lengthP = pattern.length();
int lengthS = str.length();
while (s < lengthS) {
if (p < lengthP && (pattern.charAt(p) == str.charAt(s) || pattern.charAt(p) == '?')) {
s++;
p++;
} else if (p < lengthP && pattern.charAt(p) == '*') {
starIdx = p;
match = s;
p = starIdx + 1;
} else if (starIdx != -1) {
p = starIdx + 1;
match++;
s = match;
} else {
return false;
}
}
// deal with remaining character in pattern
while (p < lengthP && pattern.charAt(p) == '*') {
p++;
}
return p == lengthP;
}
}