zoukankan      html  css  js  c++  java
  • AC自动机

    本来是早就该学的知识点了,但是拖了好长时间最近在重新捡起来。。。

     【AC自动机???自动AC机???】

    刚学OI的时候,就听到学长说AC自动机,第一次听到这个名字还以为只是开玩笑说用来自动A题的BUG。。。(相信总会有人和我想法是一样的)

    AC自动机就是字典树和KMP算法的结合,KMP实现的是单模匹配,也就是一个模式串与文本串进行匹配,但是如果我们有很多模式串怎么办呢?

    这时我们就要引入字典树(发现树结构在OI学习中贼优越的样子)

    字典树就是一棵可以表示所有出现单词的树,就像字典一样可以进行单词的查询!!!这样就可以实现多模匹配。

    但是要是我们一个个跳着来匹配字符那就GG了,和单模匹配一样的暴力,所以我们就要用KMP的失配指针来优化,定义失配指针为fail

    首先我们来看看字典树是怎么建立的(如:AAC,CAA,CAC,BA)

    那么加上其失配指针后:

    这就是将序列上的KMP变成了树上的AC自动机了 !!!

    接下来我们来看看AC自动机怎么实现的。。。

    首先我们要建立一棵字典树,Trie树:(这应该是基本操作,不会的就要看看字典树了)

    1.存储结构

    1 struct sd{
    2     int son[30],end,fail;// 每个节点的下一个字符对应的节点,如果当前节点是一个单词的结尾那就记下来,失配指针
    3 }trie[20005];
    4 char txt[1000005],ch[155][75];//模式串,文本串
    5 int vis[155],maxx,cnt,n;//每个单词的出现次数

    2.建立字典树

     1 void insert(int v)
     2 {
     3     int len=strlen(ch[v]),node=0;//用来跳的指针
     4     for(int i=0;i<len;i++)
     5     {
     6         int b=ch[v][i]-'a';
     7         if(!trie[node].son[b])//如果没出现过那就新建一个节点
     8         trie[node].son[b]=++cnt;
     9         node=trie[node].son[b];
    10     }
    11     trie[node].end=v;
    12 }

    3.预处理fail指针

     1 void getfail()
     2 {
     3      queue<int> que;//队列来BFS
     4      que.push(0);
     5      while(!que.empty())
     6      {
     7          int v=que.front();que.pop();
     8          for(int i=0;i<26;i++)
     9          {
    10              if(trie[v].son[i])
    11              {
    12                  int p=trie[v].fail;//如果有失配指针那么从失配指针开始配
    13                  while(p!=-1)
    14                  {
    15                      if(trie[p].son[i])
    16                      {trie[trie[v].son[i]].fail=trie[p].son[i];break;}//显然一个节点的失配指针的子节点也有这个节点的子节点,那就连上失配指针
    17                      p=trie[p].fail;
    18                  }
    19                  if(p==-1) trie[trie[v].son[i]].fail=0;
    20                  que.push(trie[v].son[i]);//压入队列
    21              }
    22          }
    23      }
    24 }

    4.开始匹配

     1 void cmp()
     2 {
     3     int len=strlen(txt);
     4     int a=0;//匹配指针 
     5     for(int i=0;i<len;i++)
     6     {
     7         int b=txt[i]-'a';
     8         while(a&&!trie[a].son[b]) a=trie[a].fail;//如果失配了,那就跳到失配的地方去 
     9         if(trie[a].son[b]) a=trie[a].son[b];
    10         int p=a;
    11         while(p) //在匹配指针的地方匹配一下 
    12         {
    13             if(trie[p].end) //如果有单词的结尾说明已经在字典树上匹配到了一个单词 
    14             {
    15                 vis[trie[p].end]++;//这个单词就多出现了一次 
    16                 maxx=max(maxx,vis[trie[p].end]);//取个极值 
    17             }
    18             p=trie[p].fail;
    19         }
    20     }
    21 }

    可以看看一道模板题

    【代码实现】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 using namespace std;
     5 struct sd{
     6     int son[30],end,fail,cot;
     7 }trie[20005];
     8 char txt[1000005],ch[155][75];
     9 int vis[155],maxx,cnt,n;
    10 void clear()
    11 {
    12     memset(trie,0,sizeof(trie));
    13     memset(vis,0,sizeof(vis));
    14     cnt=0,maxx=0,trie[0].fail=-1;
    15 }
    16 void insert(int v)
    17 {
    18     int len=strlen(ch[v]),node=0;
    19     for(int i=0;i<len;i++)
    20     {
    21         int b=ch[v][i]-'a';
    22         if(!trie[node].son[b])
    23         trie[node].son[b]=++cnt;
    24         node=trie[node].son[b];
    25     }
    26     trie[node].end=v;
    27 }
    28 void getfail()
    29 {
    30     queue<int> que;
    31     que.push(0);
    32     while(!que.empty())
    33     {
    34         int v=que.front();que.pop();
    35         for(int i=0;i<26;i++)
    36         {
    37             if(trie[v].son[i])
    38             {
    39                 int p=trie[v].fail;
    40                 while(p!=-1)
    41                 {
    42                     if(trie[p].son[i])
    43                     {trie[trie[v].son[i]].fail=trie[p].son[i];break;}
    44                     p=trie[p].fail;
    45                 }
    46                 if(p==-1) trie[trie[v].son[i]].fail=0;
    47                 que.push(trie[v].son[i]);
    48             }
    49         }
    50     }
    51 }
    52 void cmp()
    53 {
    54     int len=strlen(txt);
    55     int a=0;
    56     for(int i=0;i<len;i++)
    57     {
    58         int b=txt[i]-'a';
    59         while(a&&!trie[a].son[b]) a=trie[a].fail;
    60         if(trie[a].son[b]) a=trie[a].son[b];
    61         int p=a;
    62         while(p)
    63         {
    64             if(trie[p].end)
    65             {
    66                 vis[trie[p].end]++;
    67                 maxx=max(maxx,vis[trie[p].end]);
    68             }
    69             p=trie[p].fail;
    70         }
    71     }
    72 }
    73 int main()
    74 {
    75     while(scanf("%d",&n))
    76     {
    77         if(!n) break;
    78         clear();
    79         for(int i=1;i<=n;i++)
    80         scanf("%s",ch[i]),insert(i);
    81         getfail();
    82         scanf("%s",txt);
    83         cmp();
    84         printf("%d
    ",maxx);
    85         for(int i=1;i<=n;i++)
    86         if(vis[i]==maxx) printf("%s
    ",ch[i]);
    87     }
    88     return 0;
    89 }
  • 相关阅读:
    26 转义符 re模块 方法 random模块 collection模块的Counter方法
    25 正则表达式
    24 from 模块 import 名字
    24 from 模块 import 名字
    24 from 模块 import 名字
    23 析构方法 items系列 hash方法 eq方法
    21 isinstance issubclass 反射 _str_ _new_ _len_ _call_
    20 属性, 类方法, 静态方法. python2与python3的区别.
    python(1)
    python之字符串格式化
  • 原文地址:https://www.cnblogs.com/genius777/p/9050371.html
Copyright © 2011-2022 走看看