通配符匹配。简单来说就是判断字符串s和pattern字符串p是否匹配,p中的?符号表示任意一个字符,p中的*符号表示任意一个字符序列。
首先说说我自己的递推的思想:
虽然很多人说这种是动态规划的解法,但我本人并不认同这种说法,如果严格按照动态规划的定义划分的话,这个既没有最优子结构也没有重复计算的问题。不过不用在意这些细节了。
设一个二维数组bp,bp[i][j]的语义是:s的前i个字符和p的前j个字符是否匹配。
边界条件b[0][0]=true,b[0][j]=true(p的前j个字符都是*)
我们可以求解bp[i][j]:
如果p[j]是字母,则bp[i][j]=(p[j-1]==s[i-1]) && bp[i-1][j-1]
如果p[j]是?,则p[j]匹配任意一个字符,bp[i][j]=bp[i-1][j-1]
如果p[j]是*,如果*代表0个字符的话,则bp[i][j]=b[i][j-1],如果*代表多个字符的话,则s的前i个字符和p的前j个字符是否匹配,取决于s的前i-1个字符和p的前j个字符是否匹配。如果b[i-1][j]=true,则b[i][j]=true,此时即*代表的字符序列增加了一个字符s[i]而已。所以bp[i][j]=bp[i-1][j] || bp[i][j-1]
求解代码:(其实核心代码就几行,剩下的都是一些分配数组内存、释放内存、初始化之类的东西)
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
bool **bp;
//bp[i][j]:whether the first i characters of s and the first j characters of p match
bool isMatch(string s, string p) {
if(s.size()==0){
if(p.size()!=0){
for(int i=0;i<p.size();++i){
if(p[i]!='*') return false;
}
return true;
}else{
return true;
}
}else if(p.size()==0) return false;
//cout<<"s="<<s<<" p="<<p<<endl;
init(s.size(),p.size());
//edge value
bp[0][0]=true;
unsigned int tmp=1;
for(;tmp<=p.size();++tmp){
if(p[tmp-1]!='*') break;
bp[0][tmp]=true;
}
for(;tmp<=p.size();++tmp) bp[0][tmp]=false;
for(unsigned int i=1;i<=s.size();++i){
for(unsigned int j=1;j<=p.size();++j){
if(p[j-1]=='?') bp[i][j]=bp[i-1][j-1];
else if(p[j-1]!='*') bp[i][j]=(p[j-1]==s[i-1]) && bp[i-1][j-1];
else bp[i][j]=bp[i-1][j] || bp[i][j-1];
}
}
bool ret=bp[s.size()][p.size()];
//cout<<"3 bp="<<endl;
//for(unsigned int i=0;i<=s.size();++i){
//for(unsigned int j=0;j<=p.size();++j){
//cout<<bp[i][j]<<" ";
//}
//cout<<endl;
//}
//cout<<"ret="<<ret<<endl;
finish(s.size());
return ret;
}
void init(unsigned int s_size,unsigned int p_size);
void finish(unsigned int s_size);
};
void Solution::finish(unsigned int s_size){
for(unsigned int i=0;i<=s_size;++i) delete []bp[i];
delete []bp;
}
void Solution::init(unsigned int s_size,unsigned int p_size){
bp=new bool*[s_size+1];
for(unsigned int i=0;i<=s_size;++i){
bp[i]=new bool[p_size+1];
}
for(unsigned int i=0;i<=s_size;++i){
for(unsigned int j=0;j<=p_size;++j) bp[i][j]=false;
}
}
后来在网上看到一篇线性时间复杂度的解法。但是这种解法我觉得属于trick的解法了,不具备通用性。不管怎样也先记录下来吧:
我们以s="abcdcdef" p="ab*cdef"为例来说明
设i1为s的下标指针,i2为p的下标指针,初始i1=i2=0
1、s[i1]=p[i2],所以++i1,++i2
2、p[i2]=*,所以ss=i1,star=i2(star记录星号的位置,s[0]~s[ss]和p[0]~p[star]匹配,此时star所在的星号代表0个字符),i2=star+1, ++i1(继续判断i1和i2是否匹配)
3、s[i1]=p[i2],所以++i1,++i2
4、s[i1]=p[i2],所以++i1,++i2
5、s[i1]和p[i2]不匹配,所以当star位置的星号代表0个字符的时候无法匹配,所以下一步尝试star位置的星号代表一个字符。++ss,i1=ss+1,i2=star+1
6、s[i1]和p[i2]不匹配,所以当star位置的星号代表一个字符的时候也无法匹配,所以下一步尝试star位置的星号代表两个字符。++ss,i1=ss+1,i2=star+1。此时匹配。
大致就是这个思路,我根据这个求解的思路写的程序:
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
bool isMatch(string s, string p) {
unsigned int ss=0;
int star=-1;
unsigned int i1=0,i2=0;
while(i1<s.size()){
if(i2<p.size() && (p[i2]==s[i1]||p[i2]=='?')){
++i1;
++i2;
continue;
}
if(i2<p.size() && p[i2]=='*'){
ss=i1-1;
star=i2++;
continue;
}
if(star!=-1){
i2=star+1;
++ss;
i1=ss+1;
}else{
return false;
}
}
//cout<<"i1="<<i1<<" i2="<<i2<<endl;
for(;i2<p.size() && p[i2]=='*';++i2);
return i2==p.size()?true:false;
}
};