• [TJOI2013]单词 AC自动机


    题面:

    洛谷

    题解:

      很久之前做的题了,只不过之前一直90.。。。最近才发现是哪里写错了。

      我们对字符集建AC自动机。

      首先考虑一个暴力的做法,把文章当做一个长串,直接在自动机上跳,但是我们会发现,这样的复杂度可能退化到$n^2$.

      因为对于一个类似于aaaaaaaaaaaaaaaa这样的串而言,一个点的fail总是指向它的父亲,因此如果我们每次都暴力向上跳fail复杂度就不对了。

      观察到每遍历到一个节点,其实质就是给这个点到root的这条链上的每个点都+1,因此我们目标只是在fail树上对每个点都求出子树和。

      如果我们知道了所有标记的位置,显然可以建树统计一下。

      当然,也有更方便的写法,因为一个点的编号肯定比它的fail的编号大。表现在fail树上就是父亲编号比儿子小。

      所以我们从大到小枚举编号,把当前枚举到的节点的权值加给父亲即可。

      为什么这样是对的?

        因为我们可以看做是儿子先把代价给父亲,在让父亲把它的代价和它儿子的代价一起向上传。

      

      其实对于这道题而言,还有更方便的写法,你甚至不需要建匹配串。因为文章就是由给定字符集组成的,因此我们一定会遍历自动机上的每一个节点,所以与其再遍历一遍,再把每个节点权值+1,不如在建自动机的时候就直接加。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define maxn 2500500
     5 int n,ans[maxn],go[maxn],num;//num标记是第一个单词,便于处理单词重复的情况
     6 int q[maxn],head,tail,tot;//从第一位开始存文本串
     7 char s[maxn];
     8 
     9 struct ACtree
    10 {
    11     int fail[maxn], c[maxn][27];
    12     //由于是统计单词在文章中的出现次数,相同单词只算一次,所以val最大只能为1
    13     void add()//标记是第一个单词
    14     {
    15         int now=0,len=strlen(s);
    16         for(R i=0;i<len;i++)
    17         {
    18             int v=s[i]-'a';
    19             if(!c[now][v]) c[now][v]=++tot;
    20             now=c[now][v], ++ ans[now];//因为之后再遍历也是直接遍历整个树,所以直接在这里加
    21         }
    22        // if(!val[now])val[now]++;//这样val就没用了
    23         go[num]=now;//记录下每个单词的结尾部分,以防遇到重复只输出一个
    24     } 
    25 
    26     void build()
    27     {
    28         R now;
    29         for(R i=0; i<26 ;i++) 
    30             if(c[0][i]) q[++tail]=c[0][i];//既然fail一开始都为0,那就不用额外初始化了
    31         while(head<tail)
    32         {
    33             now=q[++head];
    34             for(R i=0;i<26;i++)
    35                 if(c[now][i]) fail[c[now][i]]=c[fail[now]][i],q[++tail]=c[now][i];
    36                 else c[now][i]=c[fail[now]][i];//建立虚拟节点
    37         }
    38         for(R i = tail; i; i --) ans[fail[q[i]]] += ans[q[i]];
    39         for(R i = 1; i <= n; i ++) printf("%d
    ", ans[go[i]]);
    40     }
    41     
    42 }AC;
    43 
    44 void pre()
    45 {
    46     scanf("%d",&n);    
    47     for(num=1; num<=n ;num++)
    48         scanf("%s",s), AC.add();
    49 }
    50 
    51 int main()
    52 {
    53 //    freopen("in.in","r",stdin);
    54     pre();
    55     AC.build();
    56 //    fclose(stdin);
    57     return 0;
    58 }
    View Code

      

  • 相关阅读:
    【SPOJ】6779 Can you answer these queries VII
    【SPOJ】1557 Can you answer these queries II
    【SPOJ】2916 Can you answer these queries V
    【CodeForces】86D Powerful array
    【SPOJ】1043 Can you answer these queries I
    【HDU】3727 Jewel
    【HDU】3915 Game
    【SPOJ】1043 Can you answer these queries III
    【SPOJ】2713 Can you answer these queries IV
    成为一名更好的软件工程师的简单方法
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10051894.html
Copyright © 2020-2023  润新知