leetcode题集: 44. Wildcard Matching

24 篇文章 0 订阅

题目大意:简化版的正则表达式匹配,给定一个待匹配字符串和一个pattern字符串,返回pattern字符串是否能匹配。特殊匹配符号有两个,'*' 和 '?'。

题目分析:首先看两个特殊符号的表示范围

'?': 所有单个字符;(一个坑,这里单个字符不包括空,提交次数+1)

'*': 任意字符序列,可以是空序列

考虑使用递归来解决这个问题。匹配过程分为几种情况来讨论:

1) s 匹配到了最后,且 p 也匹配到了最后,即是匹配成功,返回;

2)s 匹配到了最后,当前 p 没有匹配到最后,但是当前字符为 '*' , 此时不能确定,继续递归,p 索引增加;

3)s 匹配到最后,当前 p 没有匹配到最后,而且 p 当前索引字符不为 ‘*’ ,没有机会了,匹配失败,返回 ;

4)s 没有匹配到最后,p 匹配到了末尾,匹配失败,返回;

5)s 和 p 都没有匹配到最后:

1. 如果当前 s[i] = p[j], 继续递归i+1, j+1;

2. 如果当前 s[i] != p[j],但是p[j]='?', 继续递归i+1, j+1;

3. 如果当前 s[i] != p[j],但是p[j]='*', 进行递归,p[j] = '*' 情况下面单独考虑;

4. 如果当前 s[i] != p[j], 正常字符,匹配失败,返回

这个问题比较难的就是 '*' 字符的匹配多少问题,因为是个不确定匹配,不知道到底可以匹配多长,匹配多了或者匹配少了都会出现问题。所以需要定一个规则,这里考虑最短长度优先匹配,即从可匹配的最短长度开始逐渐增加,如果匹配成功则直接返回成功,否则进行下一个长度匹配的递归。

分析到此,很快可以写出下面的代码:

class Solution {
public:
    bool ismm(string &s, string &p, int index, int cur){
        if(index==s.size() && cur==p.size()) return true;
        else if(index==s.size() && p[cur]=='*') return ismm(s, p, index, cur+1); 
        else if(index==s.size() || cur==p.size()) return false; 
        // 当前匹配成功,进行递归匹配
        if(s[index]==p[cur]) return ismm(s, p, index+1, cur+1);
        else if(s[index] != p[cur] && p[cur]=='?'){
            // 这里刚开始以为 '?'包括空字符,所以多写了一个. 提交次数+1
            // if(ismm(s, p, index, cur+1)) return true;
            if(ismm(s, p, index+1, cur+1)) return true;
            return false;
        } else if(s[index] != p[cur] && p[cur]=='*'){
            while(cur+1 < p.size() && p[cur+1]=='*') cur++; // 去除重复的 '*'. 提交次数+1
            for(int i=index;i<s.size();i++){    // 按照最短匹配原则进行匹配
                // cout <<i <<" " <<cur+1 <<endl;
                if(ismm(s, p, i, cur+1)) return true;    //如果匹配成功,则返回
            }
            return ismm(s, p, s.size(), cur+1);    // 最长匹配,整个字符串
        } else return false;
    }
    
    bool isMatch(string s, string p) {
        return ismm(s, p, 0, 0);
    }
};

本来自己测试的都差不多了,但是这个代码还是在1708个样例时垮掉了,超时。提交次数再次+1

看来看去,也就只有在 ‘*’ 的匹配过程中出了问题。应该是某些情况下可以剪枝,使得后面的更长匹配没有进行的必要。那什么情况下才可以让更长匹配没必要进行呢?没错,那就是 s 匹配到末尾,而 p 不能匹配到末尾; p 是以最短长度优先匹配 s 的,当前选择下 s 匹配到末尾,而 p无法匹配到末尾,那更长的匹配就更不可能匹配成功了。

修改后的代码:

// 返回 1 ,表示匹配成功
// 0 表示,s 匹配到最后, 而 p 不能匹配到最后
// -1 表示匹配失败
class Solution {
public:
    int ismm(string &s, string &p, int index, int cur){
        if(index==s.size() && cur==p.size()) return 1;
        else if(index==s.size() && p[cur]=='*') return ismm(s, p, index, cur+1); 
        else if(index==s.size()) return 0;    // s 匹配到了最后,而 p 不能匹配到最后
        else if(cur==p.size()) return -1;     // 匹配失败
        // cout <<index <<" " <<cur <<endl;
        // 字符匹配 或者 p是'?'
        if(s[index]==p[cur] || p[cur]=='?') return ismm(s, p, index+1, cur+1);
        else if(p[cur]=='*'){
            while(cur+1 < p.size() && p[cur+1]=='*') cur++;    //去除重复的'*'
            for(int i=index;i<s.size();i++){
                int ret = ismm(s, p, i, cur+1);       
                if(ret>=0) return ret; // 必要剪枝,不光只有匹配成功返回,0 情况也直接返回
            }
            return ismm(s, p, s.size(), cur+1);
        } 
        return -1;
    }
    
    bool isMatch(string s, string p) {
        int slen = s.size(), plen = p.size();
        if(slen>0 && plen>0 && s[slen-1] != p[plen-1] && p[plen-1]!='*' && p[plen-1]!='?')    //做个简单的筛选,如果最后一个字符不匹配,就不要递归求了,费劲
            return false;
        return ismm(s, p, 0, 0)>0;
    }
};

最后终于是提交成功了,一群什么魔鬼,居然这么快!!!菜到自闭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值