这题以前做了好久都没搞定,题解方法是按照*将字符串分割然后贪心匹配,匹配时要修改kmp函数,改了半天还是过不了,网上多数方法都是错误的,数据加强以后都是WA,就一直放着,今天看AC自动机论文,发现AC自动机可以模糊匹配带有'?'的字符串,然后就把这题拉出来A掉了~~
AC自动机匹配带有问号通配符的方法理解起来还是比较简单的,英文ppt这里下载
对于带'?'通配符的字符串:
?ATC??TC?ATC
将它按'?'可以分成三个字串
{ATC},{TC},{ATC}
将这三个字符串构造字典树,然后按照AC自动机的一般方法构造fail指针
每一个串的末尾字符记录该字符(以及他的fail指针指向的字符)在原串中的位置,如图:
3节点是第一个子串末尾(位置4,从1开始计数),和第三个子串末尾(位置12),以及fail指向的第二个子串末尾(位置8)
5节点是第二个子串的末尾,位置8
就这样做好后去查询原串,如
ACGATCTCTCGATC
在AC自动机上查询时每到一个点就可以通过他记录的值判断他起始点所在的位置,在这个位置加1,比如在遍历到最后一个字符(第13个字符)时自动机走到3点,那么可以推出这时串起点可能在10的位置(串1的末尾),2的位置(串3的末尾),6的位置(串2的末尾),在这些位置均加1,最后得到串每个位置对应的cnt值:
1 0 3 0 0 0 1 0 0 0 1 0 0 0
对于每一位的cnt值也就是说能和这一位对上的子串的个数,如果等于分割子串个数也就找到了这样的一个子串起点,在处理时注意前端数组越界的情况,还有就是对于串末尾有'?'的情况,要在求出值后要判断一下是否超出原串长度范围
复杂度:建树是O(n)的,得到fail指针也是O(n),查询也是O(n),虽然说会被分为k段,但是分析下的话每个字符也只会被遍历到一遍,所以总的复杂度也是O(n)的
这题按照题解所说的将字符串按照'*'分解后再对每一段做上述的操作,即可求得结果,还没想到如何直接一次性搞定'*'和'?',很烂的代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 using namespace std; 5 #define N 100005 6 #define B 26 7 8 int tree[N][B],size; 9 vector<int> key[N]; 10 int cnt[N],tcnt; 11 void build(char str[]){ 12 size=tcnt=0; 13 memset(tree[0],0,sizeof(tree[0])); 14 key[0].clear(); 15 16 int node=0; 17 for(int i=0;!i||str[i-1];i++){ 18 if(str[i]=='?'||str[i]==0){ 19 if(node){ 20 key[node].push_back(i-1); 21 node=0; tcnt++; 22 } 23 continue; 24 }else{ 25 int c=str[i]-'a'; 26 if(!tree[node][c]){ 27 tree[node][c]=++size; 28 memset(tree[size],0,sizeof(tree[size])); 29 key[size].clear(); 30 } 31 node=tree[node][c]; 32 } 33 } 34 } 35 36 int que[N]; 37 int fail[N]; 38 void getfail(){ 39 int *s=que,*e=que; 40 for(int i=0;i<B;i++){ 41 if(tree[0][i]){ 42 fail[tree[0][i]]=0; 43 *e++=tree[0][i]; 44 } 45 } 46 while(s!=e){ 47 int p=*s++; 48 for(int i=0;i<B;i++){ 49 if(tree[p][i]){ 50 int v=tree[p][i]; 51 *e++=v; 52 fail[v]=tree[fail[p]][i]; 53 if(key[fail[v]].size()){ 54 key[v].insert(key[v].end(), 55 key[fail[v]].begin(),key[fail[v]].end()); 56 } 57 }else{ 58 tree[p][i]=tree[fail[p]][i]; 59 } 60 } 61 } 62 } 63 64 int find(char str[]){ 65 if(tcnt==0) return 0; 66 int node=0; 67 for(int i=0;str[i];i++){ 68 node=tree[node][str[i]-'a']; 69 cnt[i] = 0; 70 for(int j=0;j<key[node].size();j++){ 71 if(i>=key[node][j]){ 72 cnt[i-key[node][j]]++; 73 if(cnt[i-key[node][j]]==tcnt) return i-key[node][j]; 74 } 75 } 76 } 77 return -1; 78 } 79 80 char tmp[N]; 81 char src[N]; 82 char wild[N]; 83 bool match(int ls, int lp){ 84 int tl=0; 85 for(int i=0;i<lp;i++) if(wild[i]!='*') tl++; 86 if(tl>ls) return false; 87 88 int s=-1; 89 for(int i=0;i==0||src[i-1];i++){ 90 if((src[i]!=0&&wild[i]=='?')||src[i]==wild[i]) continue; 91 if(wild[i]=='*') s=i+1; 92 else return false; 93 break; 94 } 95 if(s==-1) return true; 96 97 for(int i=ls-1,j=lp-1;;i--,j--){ 98 if(i==-1&&j==-1) break; 99 if(i==-1){ 100 if(wild[j]!='*') return false; 101 break; 102 } 103 if(j==-1) return false; 104 if(src[i]==wild[j]||wild[j]=='?'){ 105 src[ls=i] = wild[j] = 0; 106 } else if(wild[j]=='*') break; 107 else return false; 108 } 109 110 int ts=s-1; 111 for(int i=1;i<lp-tl;i++){ 112 if(wild[s]=='*'){s++;continue;} 113 int len = 0; 114 for(;wild[s]!='*'&&wild[s];s++){ 115 tmp[len]=wild[s]; 116 tmp[++len]=0; 117 } 118 s++; 119 build(tmp); 120 getfail(); 121 int pos=find(src+ts); 122 if(pos==-1) return false; 123 else{ 124 ts+=pos+len; 125 if(ts>ls) return false; 126 } 127 } 128 return true; 129 } 130 131 int main(){ 132 while(~scanf("%s", src)){ 133 scanf("%s", wild); 134 puts(match(strlen(src), strlen(wild))?"YES":"NO"); 135 } 136 }
本文地址:http://www.cnblogs.com/ambition/archive/2011/08/26/Wildcard.html