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 }
  • 相关阅读:
    微信小程序入门注意
    回调函数理解实例
    2017最新教程--如何下载美拍视频
    tomcat如何利用waf进行防护
    深入理解正则表达式-----应用于检测csrf的正则表达式
    snort安装--daq,dnet---ERROR! dnet header not found, go get it from...等错误解决方案
    先知安全技术社区原创文章奖励计划
    谈一谈情报威胁与态势感知
    pfring破解DNA限制
    八皇后--python代码
  • 原文地址:https://www.cnblogs.com/genius777/p/9050371.html
Copyright © 2011-2022 走看看