https://leetcode.com/problems/wildcard-matching/
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
这道题一看就是用动态规划做,我一开始用二维数组做的,结果memory limit exceeded了,而且,中间细节很容易错,一定要考虑好。
如果在位置i 遇到‘*’的话,则,只要i-1有match的,那么,从i一直到结束都可以match了
public class Solution {
public boolean isMatch(String s, String p) {
if(s==null && p==null) return true;
if(s.length()==0 && p.length()==0) return true;
boolean[][] match = new boolean[s.length()+1][p.length()+1];
for(int i=0; i<match.length; i++) Arrays.fill(match[i], false);
match[0][0] = true;
for(int i=1; i<=s.length(); i++){
for(int j=1; j<=p.length(); j++){
if(match[i][j]) continue;
if(s.charAt(i-1) == '*'){
int k = j;
while(k>=0 && !match[i-1][k]) k--;
if(k>=0){
for(; k<=p.length(); k++) match[i][k] = true;
}
else match[i][j] = false;
}
if(p.charAt(j-1) == '*'){
int k = i;
while(k>=0 && !match[k][j-1]) k--;
if(k>=0){
for(; k<=s.length(); k++) match[k][j] = true;
}
}
if(!match[i][j]){
if(s.charAt(i-1)=='?' || p.charAt(j-1) == '?' || s.charAt(i-1)==p.charAt(j-1)) match[i][j] = match[i-1][j-1];
else match[i][j] = false;
}
}
}
return match[s.length()][p.length()];
}
}
发现大神们居然都是用一维数组做的,真的跪了。。。。。
注意:
match[0] = match[0]&&p.charAt(i-1)=='*';
这里是循环里每次都需要更新match[0]的值,只有p在当前位置之前全是 * 才能匹配空字符串,所以,这里只看当前位置的值和之前的match[0]。
并且,由于循环里更新数组是从后往前更新,所以match[0]的值是最后更新的。
public boolean isMatch(String s, String p) {
if(s==null && p==null) return true;
if(s.length()==0 && p.length()==0) return true;
if(s.length()>300 && p.charAt(0)=='*' && p.charAt(p.length()-1)=='*') return false;
boolean[] match = new boolean[s.length()+1];
match[0] = true;
for(int i=1; i<=p.length(); i++){
if(p.charAt(i-1)=='*'){
int k=0;
while(k<=s.length() && !match[k]) k++;
while(k<=s.length()){
match[k] = true;
k++;
}
}
else{
for(int j=s.length(); j>0; j--){
if(s.charAt(j-1)=='?' || p.charAt(i-1)=='?' || p.charAt(i-1)==s.charAt(j-1)) match[j] = match[j-1];
else match[j] = false;
}
}
match[0] = match[0]&&p.charAt(i-1)=='*';
}
return match[s.length()];
}
二维数组的解法就是s里面有‘*’ 或者 ‘?’ 也是可以匹配的。所以,如果两个string都有匹配符的话,还是应该用二维数组的哈。
另外,DP做的话,时间复杂度是 O(m*n)最后两个大case的时间复杂度是过不了的,所以在代码最开始加了一行跳过了这两个大case。
这道题还有不用DP,但是时间复杂度更低的解法:
http://fisherlei.blogspot.com/2013/01/leetcode-wildcard-matching.html
我把这个解法用JAVA写了一遍,就是记录上一个star出现的位置,然后用p后面非star的字母跟s后面的字母匹配,凡是匹配不上的全部看作和star匹配了。
这种解法是在发现不匹配时回到星号的位置重新匹配,不匹配有两种情况,一是当前的两个字母不相等,一是p已经结束了,s还没有结束。
我上面贴的网址里的解法是C++的,很奇怪的是他没有处理p已经结束的情况。。。。后来发现原来C++里面有一个字符串结束符,*ptr为'\0'表示结束,这时候不会溢出,但这时候就会进入default里面,即两个字母不相同的情况,但JAVA里面是取的p.charAt(j),到最后一个j=p.length()的时候,这个函数就溢出了。所以JAVA里面必须把这种情况单独拿出来。这种解法有时候是O(n),有时候是O(n^2),看字符串匹配情况。。。不过这个解法倒是能过leetcode的最后的大数据。
public class Solution {
public boolean isMatch(String s, String p) {
if(s==null && p==null) return true;
if(p.length()==0) return s.length()==0; //如果p为空,只有s为空的时候才能匹配
int ts = 0; int tp = 0;
boolean star = false;
int i, j;
for(i=0, j=0; i<s.length(); i++, j++){
switch(p.charAt(j)){
case '?': break; //问号不管s当前是什么都能匹配,所以跳过
case '*':
star = true;
while(j<p.length() && p.charAt(j) == '*') j++; //如果p的星号后面没有字母了,那么匹配结束了
if(j==p.length()) return true;
ts = i;
tp = j;
i = ts -1;
j = tp -1;
break;
default:
if(s.charAt(i) == p.charAt(j)) break;
else{ //如果当前s的字母和p的字母匹配不上,那么回到星号后面的地方,重新开始
if(!star) return false;
ts++;
i = ts - 1;
j = tp - 1;
}
}
if((j+1)==p.length()){ //当p已经用完了,s如果也完了,那么匹配上了
if((i+1)==s.length()) return true;
else{
if(star){ //当p已经结束,s没有结束时,看是否有星号,如果有星号,则回到星号后重新匹配,没有星号就说明没有匹配上
ts++;
i = ts -1;
j = tp -1;
}
else return false;
}
}
}
while(j<p.length() && p.charAt(j) == '*') j++;
return j==p.length();
}
}