题目大意:简化版的正则表达式匹配,给定一个待匹配字符串和一个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;
}
};
最后终于是提交成功了,一群什么魔鬼,居然这么快!!!菜到自闭