HDU 3901 Wildcard AC自动机通配符匹配

这题以前做了好久都没搞定,题解方法是按照*将字符串分割然后贪心匹配,匹配时要修改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 

posted @ 2011-08-26 22:38  Amb@HDU  阅读(...)  评论(...编辑  收藏