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
全匹配问题,也就是正则式的问题,题目要求两个字符串是否匹配,?代表任意一个字母,*代表任意个字母,也可以是0个。
第一次写的时候出现了很多问题,我用了递归,但由于*代表的是任意字母组合,因此也就比较麻烦,而第一次写出的代码,虽然在本地可以运行成功,但是提交的时候超时。代码如下:
public static boolean isMatch(String s, String p) { char[] str1 = s.toCharArray(); char[] str2 = p.toCharArray(); int num = 0; for( int i =0;i<p.length();i++){ if( str2[i] == '*') num++; } num = s.length() - p.length() + num; System.out.println( s.length()+" "+p.length()+" "+num); return Match(str1,str2,0,0,num); } public static boolean Match(char[] str1,char[] str2,int a,int b,int num) {//num 表示*可以代表的字符数 if ( num<0 ) return false; int len1 = str1.length,len2 = str2.length; if( a == len1 && b == len2)//两个数组都走到头了 return true; if( a == len1 && str2[b] != '*')//只有第一个数组走到头了 return false; else if ( a == len1 && str2[b] == '*'){ while( b < len2 && str2[b] == '*') b++; if( b == len2) return true; else return false; } if( b == len2 ) //只有第二个数组走到头了 return false; if( str1[a] == str2[b] || str2[b] == '?'){//遇到相同的字母或者? while( a<len1 && b<len2 && str1[a] == str2[b]){ a++; b++; } return Match(str1,str2,a++,b++,num); } if( str2[b] == '*'){ //遇到* while( b<len2 && str2[b] == '*') //连着的*相当于一个* b++; if( b == len2 ) return true; for( int i = a,j = b,k = num;i<len1 ;){ k = num; j = b; while( i<len1 && j<len2 && str1[i] != str2[b]){ //寻找*后匹配str[b]的字符 i++; k--; } while( i<len1 && j<len2 && (str1[i] == str2[j] || str2[j] == '?' )){ //之后的字符也要匹配 i++; j++; } if( k<0) continue; if( i<len1 && j<len2 && str1[i] != str2[j] && str2[j] != '*')//如果不匹配是因为字符不一样且不是? continue; else if( Match(str1,str2,i,j,k) ) return true; } } return false; }
然后发现了关键的一点: 1.当str1与str2分别到了i和j的时候。同时在str2[j]的位置遇到了*(后面还有别的*的情况下)。
2.之前的方法是,找str1[i](包括str1[i])之后遇到的所有的str2[j]之后的第一个字母a(非*),然后再按照之前的规则进行匹配,遇到下一个*的时候也会按照刚才的规则进行判断,一旦,错误,将回到第一个*的地方重新进行判断。
3.这里就存在一个可以优化的点,就是在遇到下一个*的时候,如果已经匹配成功,那么就算之后匹配失败,其实也不用重新返回第一个*来进行判断,只需要回到当前的*处接着判断就可以了,这样的话其实不用递归会比较好写一点。
代码如下:
public static boolean isMatch(String s, String p) { int len1 = s.length(),len2 = p.length(); char[] str1 = s.toCharArray(); char[] str2 = p.toCharArray(); int i = 0, j = 0, pre = -1,before_i = -1,before_j = -1; while( i < len1 ){ if( j < len2 && (str1[i] == str2[j] || str2[j] == '?')){ i++; j++; } else if( j < len2 && str2[j] == '*'){ before_i = i; before_j = j; j++; }else if( before_i != -1){ i = before_i; j = before_j+1; before_i++; }else return false; } while( j<len2 && str2[j] == '*') j++; if( j == len2 ) return true; else return false; }
最后是基本接近最快。