LeetCode - Wildcard Matching

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()];
    }


不过第二个一维数组的解有个问题,就是被匹配的string s里面不能有‘*’ 或者 '?' ,但是leetcode 也能过,可能这题目的意思就是s里面不能有这些匹配符,只是没有说清楚吧。

二维数组的解法就是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();
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值