洛谷 P3966 [TJOI2013]单词(AC自动机 || Fail树)

题目链接:https://www.luogu.com.cn/problem/P3966

 

对于这一道题,可以对所有单词建出AC自动机,然后将每个节点x和fail[x]之间连一条边,这样可以形成一个树:fail树。

根据fail树的一些性质:

①:每个节点都是一个字符串的前缀,并且每个字符串的前缀一定在fail树上有一个节点

②:fail树的大小等于AC自动机的大小,fail树的根就是AC自动机的根

③:每个节点的父亲都是这个节点的最长后缀,每个节点的所有祖先是这个节点的所有后缀

------>尤其是第3个性质,我们可以把这个问题转化成:

只要将每个单词加入字典树,中间经过的节点sum值全部++,然后建立AC自动机求出fail树,从根节点开始DFS,对于树上每个节点,将它和它子树中所有的sum加起来就是该节点单词出现总次数。

 

AC代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
 7 const int N=1000000+100;
 8 int fail[N],ch[N][26],vis[N],ans[N],sum[N],head[N];
 9 char str[N];
10 int cnt,tot;
11 struct node{
12     int to,next;
13 }edge[N*2];
14 void init(){
15     memset(head,-1,sizeof(head));
16     tot=0;
17 }
18 void add(int u,int v){
19     edge[tot].next=head[u];
20     edge[tot].to=v;
21     head[u]=tot++;
22 }
23 void insert(char *s,int v){
24     int u=0;
25     int len=strlen(s);
26     for(int i=0;i<len;i++){
27         int id=s[i]-'a';
28         if(!ch[u][id]) ch[u][id]=++cnt;
29         u=ch[u][id];
30         sum[u]++;
31     }
32     vis[v]=u;
33 }
34 void get_fail(){
35     int u=0;
36     queue<int> q;
37     for(int i=0;i<26;i++){
38         if(ch[u][i]){
39             q.push(ch[u][i]);
40             fail[ch[u][i]]=u;
41             add(0,ch[u][i]);
42         }
43     }
44     while(!q.empty()){
45         int u=q.front();q.pop();
46         for(int i=0;i<26;i++){
47             if(ch[u][i]){
48                 q.push(ch[u][i]);
49                 fail[ch[u][i]]=ch[fail[u]][i];
50                 add(fail[ch[u][i]],ch[u][i]);
51             }
52             else ch[u][i]=ch[fail[u]][i];
53         }
54     }
55 }
56 void DFS(int u,int fa){
57     for(int i=head[u];i!=-1;i=edge[i].next){
58         int v=edge[i].to;
59         if(v==fa) continue;
60         DFS(v,u);
61         sum[u]+=sum[v];
62     }
63 }
64 int main(){
65     int n;
66     scanf("%d",&n);
67     for(int i=1;i<=n;i++){
68         scanf("%s",str);
69         insert(str,i);
70     }
71     init();
72     get_fail();
73     DFS(0,-1);
74     for(int i=1;i<=n;i++) printf("%d\n",sum[vis[i]]);
75     return 0;
76 }
AC代码

 

posted @ 2020-03-03 20:44  dfydn  阅读(229)  评论(0编辑  收藏  举报