指针版-AC自动机-Keyword Search|玄武密码

大家都不喜欢指针,但是这个AC自动机仿佛不用不行……

先引用我最喜欢的话:“AC自动机,不是自动AC的机器。” 如果写不好还可能一直WA

AC自动机是KMP与Trie树的完美结合,适用于多字符串匹配,这与KMP的单串不同

首先是一棵Trie树

Trie树

记得建空节点做根,有大神忘记了建,直想砸键盘。

然后标准建树

如果用指针的话申请空间冷静一点

下一步就是构建fail指针,虽然我喜欢把它叫next,与KMP一致

幸好找到了好的版子,不然就很难搞

因为fail指针的构建顺序必须由上到下

即:fail是从下面的节点指向上面的节点的,这样不会漏配

So用BFS会很棒。

左面就是一个手画的树


void build(){
    root->next=NULL;//根节点无fail
    push(root);//根节点入队
    while(!empty()){//BFS开始
        TREE *i=front();//提取队首元素
        for(int j=0;j<26;j++){//对每一个字母进行处理,尝试搜索
            if(i->s[j]!=NULL){//搜到
                if(i==root)i->s[j]->next=root;//是和根节点直接连的回根
                else
                {
                    TREE* p=i->next;//和搜索的点的fail指针的下节点找
                    while(p!=NULL)//继续找,所有fail都符合要求 
                    {
                        if(p->s[j]!=NULL)//fail指针处有和j字母一样的点 
                        {
                            i->s[j]->next=p->s[j];//更新结束 
                            break;
                        }
                        p=p->next;//fail的fail也符合要求 
                    }
                    if(p==NULL) i->s[j]->next=root;//无匹配,回根 
                }
                q[++e]=i->s[j];//结果入队 
            }
        }
    }
}

经过一番建立,我们将所有的fail构建完成~~

长这样:

ACauto最好自己再画一个图,这样会更好的理解

然后就要搜索了,搜索和建立很像

补下:搜索的解释,上次忘写了,自己很吃亏,尴尬

void ffind(){//搜索函数 
    int j=0;//从0号位开始搜 
    TREE *p=root;//另一方面树上从根节点走起 
    while(st[j])
    {
        int i=st[j]-'a';//不解释 
        while(p->s[i]==NULL && p!=root) p=p->next;//没找着,就顺着fail往下走 
        p=p->s[i];//这时只可能有两种可能,一是真没有,二是找着一个 
        if(p==NULL)p=root;//没有我们直接回根,相当于重置树上指针 
        TREE *k=p;//这个是找着后用的 
        while(k!=root && k->endt!=-1)//去找 
        {
            ans+=k->endt;//有结尾标记,这个是根据KeywordSearch的题意 
            k->endt=-1;//为了防止重复计算 ,这个也是题意 
            k=k->next;//到所有的fail指针处找 
        }
        j++;
    }
}

如果是搜是否匹配或出现次数,在结尾写cnt计数即可

但如果要算匹配长度,我们就要打标记,再dfs一遍,像玄武密码一样

下面补上题和码

[模板]Keyword Search

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <queue>
  5 #define  L 1010101 
  6 using namespace std;
  7 struct TREE{
  8     TREE *next,*s[26];
  9     char dat;
 10     int endt;
 11     TREE(){
 12         next=NULL;
 13         memset(s,0,sizeof s);
 14         dat=endt=0;
 15     }
 16 };
 17 TREE *q[9999999];
 18 int f=0,e=0;
 19 TREE *root;
 20 int n,ans=0;
 21 char ch[L],st[L];
 22 void add(char *now){
 23     TREE *i=root;
 24     int j=0;
 25     while(now[j]){
 26         int k=now[j]-'a';
 27         if(i->s[k]==NULL)
 28             i->s[k]=new TREE();
 29         i=i->s[k];
 30         j++;//cout<<j<<endl;getchar();puts("P");
 31     }
 32     i->endt++;
 33 }
 34 bool empty(){
 35     if(e==f){
 36         return true;
 37     }
 38     return false;
 39 }
 40 void push(TREE *i){
 41     e++;
 42     q[e]=i;
 43 }
 44 TREE* front(){
 45     if(empty())return NULL;
 46     f++;
 47     return q[f];
 48 }
 49 void build(){
 50     root->next=NULL;
 51     push(root);
 52     while(!empty()){
 53         TREE *i=front();
 54         for(int j=0;j<26;j++){
 55             if(i->s[j]!=NULL){
 56                 if(i==root) i->s[j]->next=root;
 57                 else
 58                 {
 59                     TREE* p=i->next;
 60                     while(p!=NULL)
 61                     {
 62                         if(p->s[j]!=NULL)
 63                         {
 64                             i->s[j]->next=p->s[j];
 65                             break;
 66                         }
 67                         p=p->next;
 68                     }
 69                     if(p==NULL) i->s[j]->next=root;
 70                 }
 71                 q[++e]=i->s[j];
 72             }
 73         }
 74     }
 75 }
 76 void ffind(){
 77     int j=0;
 78     TREE *p=root;
 79     while(st[j])
 80     {
 81         int i=st[j]-'a';
 82         while(p->s[i]==NULL && p!=root) p=p->next;
 83         p=p->s[i];
 84         if(p==NULL)p=root;
 85         TREE *k=p;
 86         while(k!=root && k->endt!=-1)
 87         {
 88             ans+=k->endt;
 89             k->endt=-1;
 90             k=k->next;
 91         }
 92         j++;
 93     }
 94 }
 95 int main(){
 96     int T;
 97     scanf("%d",&T);
 98     while(T--){
 99         root=new TREE();
100         ans=0;        
101         scanf("%d",&n);
102         for(int i=1;i<=n;i++){
103             scanf("%s",ch);
104             add(ch);
105         }
106         scanf("%s",st);
107         build();
108         ffind();
109         printf("%d
",ans);        
110     } 
111     return 0;
112 }
View Code

[稍有思考][还是模板]玄武密码

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #define L 111111111
  5 #define N 111111
  6 using namespace std;
  7 struct ACauto{
  8     ACauto *next,*s[4];
  9     int endp,DeepinC;
 10     bool mark;
 11     ACauto(){
 12         next=NULL;
 13         memset(s,0,sizeof s);
 14         mark=DeepinC=endp=0;
 15     }
 16 }*root;
 17 struct Myqueue{
 18     ACauto *Q[9999999];int f,e;
 19     bool empty(){
 20         if(e==f)return true;
 21         return false;
 22     }
 23     void push(ACauto *k){
 24         e++;
 25         Q[e]=k;
 26     }
 27     ACauto* front(){
 28         if(empty())return NULL;
 29         f++;
 30         return Q[f];
 31     }
 32 }q;
 33 int n,ans=0,cnt=0;
 34 char st[L],ch[N][111];
 35 inline int turn(char ch){
 36     switch(ch){
 37         case 'E':return 0;
 38         case 'W':return 1;
 39         case 'S':return 2;
 40         default :return 3;
 41     }
 42 }
 43 void add(char *c){
 44     ACauto *i=root;
 45     int j=0;
 46     while(c[j]){
 47         int k=turn(c[j]);
 48         if(i->s[k]==NULL)
 49             i->s[k]=new ACauto();
 50         i=i->s[k];
 51         i->DeepinC=j+1;
 52 //        cout<<c[j]<<" "<<i->DeepinC<<endl;
 53         j++;
 54     }
 55     i->endp++;
 56 }
 57 void build(){
 58     root->next=NULL;
 59     q.push(root);
 60     while(!q.empty()){
 61         ACauto *i=q.front();
 62         for(int j=0;j<4;j++){
 63             if(i->s[j]!=NULL){
 64                 if(i==root) i->s[j]->next=root;
 65                 else{
 66                     ACauto *p=i->next;
 67                     while(p!=NULL){
 68                         if(p->s[j]!=NULL){
 69                             i->s[j]->next=p->s[j];
 70                             break;
 71                         }
 72                         p=p->next;
 73                     }
 74                     if(p==NULL) i->s[j]->next=root;
 75                 }
 76                 q.push(i->s[j]);
 77             }
 78         }
 79     }
 80 }
 81 void ffind(char *ser){
 82     int j=0;
 83     ACauto *p=root;
 84     while(ser[j]){
 85         int i=turn(ser[j]);
 86         while(p->s[i]==NULL&&p!=root){
 87             p=p->next;
 88             p->mark=1;//cout<<p<<" INP  "<<j<<" "<<ser[j]<<" Depth: "<<p->mark<<" "<<p->DeepinC<<endl;
 89         }
 90         p=p->s[i];
 91         if(p==NULL)p=root;
 92         ACauto *k=p;
 93         while(k!=root && k->endp!=-1){
 94             k->mark=1;
 95             k->endp=-1;
 96             k=k->next;//cout<<k<<" INK  "<<j<<" "<<ser[j]<<" Depth: "<<k->mark<<" "<<k->DeepinC<<endl;
 97         }
 98 //        cout<<k<<" OUTK "<<j<<" "<<ser[j]<<" Depth: "<<k->mark<<" "<<k->DeepinC<<endl;
 99         j++;
100     }
101 }
102 void ser(char *c){
103     ACauto *i=root;
104     int j=0;
105     int lsans=0;
106     bool la=1;
107     while(c[j]){
108         int k=turn(c[j]);
109         i=i->s[k];
110         if(i->mark)ans=max(i->DeepinC,ans);
111         j++;
112     }
113 }
114 int main(){
115     int len;
116     root=new ACauto();
117     scanf("%d%d",&len,&n);
118     scanf("%s",st);
119     for(int i=1;i<=n;i++) {
120         scanf("%s",ch[i]);
121         add(ch[i]);
122     }
123     build();
124     ffind(st);
125     for(int i=1;i<=n;i++){
126         ans=0;
127         ser(ch[i]);
128         printf("%d
",ans);
129     }
130     return 0;
131 }
View Code
Miemeng真的蒻
原文地址:https://www.cnblogs.com/kalginamiemeng/p/11021748.html