• hdu2222 Keywords Search(AC自动机初步)


    题目大意:

      给出多个模式串和一个主串,求多少个模式串在主串中出现过。


      传送门

    这是一道AC自动机的模板题。

    在学习AC自动机之前,首先要学习WA自动机、TLE自动机和MLE自动机(雾

    AC自动机是一种多模式串匹配算法。

    AC自动机概述:

      *fail指针:指向失配时的匹配节点;

      1)构建字典树

      2)初始化fail指针:

        一条$fail$指针链可以理解为一个串连所有后缀相同的字符串的链表,并且所有链表的末端都指向trie的根。我们定义沿着节点$v$的$fail$指针走到根部的路径为v的trie链表

        虚线为$fail$指针,红色节点为AC-DFA的接收态。

        使用BFS构造fail指针:初始化队列为树根。对于trie上的每个非叶子节点$u$的孩子$v$,我们沿着其fail链表向上走,直到某个节点的孩子$x=v$,则$v->fail=x$。将$v$加入队列。

        特别的,对于$p->nxt[i]==NULL$的节点,令$p->nxt[i]=p->fail->nxt[i]$

      3)由于初始化$fail$指针的时候,令$p->nxt[i]=p->fail->nxt[i]$,所以现在的trie树变成了一个trie图,所有节点的$nxt$指针都不为空。我们按照主串,沿着相应的路径向走trie图,每访问一个节点就遍历该节点的$fail$链表并计数。

    代码:

     1 #include<cstring>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cctype>
     6 #define foru(i,x,y) for(int i=x;i<=y;i++)
     7 const int N=1e6+10;
     8 
     9 struct trie{
    10     trie *nxt[26];
    11     trie *fail;
    12     int v;
    13     void init(){v=0;foru(i,0,25)nxt[i]=NULL;fail=NULL;}
    14 };
    15 trie *r,*q[600000];
    16 int T,n;
    17 char ch[N];
    18 void add(char *s){//建trie 
    19     trie *k=r,*p;
    20     int l=strlen(s);
    21     foru(i,0,l-1){
    22         int id=s[i]-'a';
    23         if(!k->nxt[id]){
    24             p=(trie*)malloc(sizeof(trie));
    25             p->init();
    26             k->nxt[id]=p;
    27             k=p;
    28         }else{
    29             k=k->nxt[id];
    30         }
    31     }
    32     k->v++;//标记为接收态 
    33 }
    34 
    35 void setfail(){
    36     trie *k;
    37     int s=1,t=0;
    38     q[++t]=r;
    39     while(s<=t){
    40         k=q[s++];
    41         foru(i,0,25){
    42             if(k->nxt[i]){
    43                 trie *p=k->fail;
    44                 while(p&&!p->nxt[i])p=p->fail;//寻找上一个相同后缀的字符串 
    45                 k->nxt[i]->fail=(!p?r:p->nxt[i]);//不存在则指向trie根 
    46                 q[++t]=k->nxt[i];
    47             }else
    48                 k->nxt[i]=(k==r?r:k->fail->nxt[i]);
    49         }
    50     }
    51 }
    52 
    53 int find(char *s){
    54     trie *k=r,*p;int ret=0;
    55     int l=strlen(s);
    56     foru(i,0,l-1){
    57         int id=s[i]-'a';
    58         k=k->nxt[id];
    59         p=k;
    60         while(p){//遍历fail链 
    61             ret+=p->v;
    62             p->v=0;//避免重复计数 
    63             p=p->fail;
    64         }
    65     }
    66     return ret;
    67 }
    68 
    69 int main(){
    70     scanf("%d",&T);
    71     while(T--){
    72         r=(trie*)malloc(sizeof(trie));
    73         r->init();
    74         scanf("%d",&n);
    75         foru(i,1,n){
    76             scanf("%s",ch);
    77             add(ch);
    78         }
    79         setfail();
    80         scanf("%s",ch);
    81         printf("%d
    ",find(ch));
    82     }
    83 }
  • 相关阅读:
    东南亚互联网创业哪些领域比较有前景?
    2021最新最实用.NET Core笔试题附答案
    2021UpdateC#.NET笔试题基础篇
    Ubuntu下 安装MiniGUI
    MiniGUI 如何显示繁体字
    Android 基础一 TextView,Style样式,Activity 传值,选择CheckBox 显示密码
    【玩转开源】BananaPi R2 —— 第一篇 Openwrt安装
    JAVA面对对象(二)——继承、方法的覆写
    JAVA面对对象(一)——封装
    JAVA关键字及作用
  • 原文地址:https://www.cnblogs.com/y-m-y/p/7478391.html
Copyright © 2020-2023  润新知