[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\n", 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

 

  

posted @ 2018-12-02 00:17  ww3113306  阅读(145)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。