zoukankan      html  css  js  c++  java
  • 指针版-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真的蒻
  • 相关阅读:
    使用LAMP创建基于wordpress的个从博客网站
    【solr基础教程之一】Solr相关知识点串讲
    solr源码导入eclipse
    【Nutch2.2.1基础教程之2.2】集成Nutch/Hbase/Solr构建搜索引擎之二:内容分析
    设置secureCRT中vim的字体颜色
    何时使用hadoop fs、hadoop dfs与hdfs dfs命令
    Hadoop常见异常及其解决方案
    Java HashMap遍历的两种方式
    android json 解析简单实例
    Android客户端与数据库交互数据的简单学习
  • 原文地址:https://www.cnblogs.com/kalginamiemeng/p/11021748.html
Copyright © 2011-2022 走看看