3172 单词
AC自动机模板题,因为i的fail指向j说明j的深度比i小,也就是说明j的后缀一定在i中也出现过,所以add一条j指向i的边,最后答案即为该字符串最后一个字母所在AC自动机节点的子树和。
/**************************************************************
Problem: 3172
User: syh0313
Language: C++
Result: Accepted
Time:444 ms
Memory:309244 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
using
namespace
std;
const
int
maxn=2000010;
int
n,root=1,topt,head,tail,q[maxn*20],cnt=1;
int
st[maxn],nt[maxn],to[maxn],num[210];
bool
f[maxn];
string s[210],ss;
struct
data{
int
v,c[30],fa;}a[1000010];
void
add(
int
x,
int
y)
{to[++topt]=y; nt[topt]=st[x]; st[x]=topt;}
void
insert(
int
k)
{
int
len=ss.length();
int
now=root;
for
(
int
i=0;i<len;i++)
{
int
x=ss[i]-
'a'
+1;
if
(!a[now].c[x]) a[now].c[x]=++cnt;
now=a[now].c[x];
a[now].v++;
}
num[k]=now;
return
;
}
void
getfail()
{
head=1; tail=1; q[1]=root;
while
(head<=tail)
{
int
now=q[head++];
for
(
int
i=1;i<=26;i++)
{
int
to=a[now].c[i];
if
(!to)
continue
;
if
(now==root) {a[to].fa=root; add(root,to);}
else
{
int
father=a[now].fa;
while
(father)
{
if
(a[father].c[i])
{a[to].fa=a[father].c[i]; add(a[to].fa,to);
break
;}
father=a[father].fa;
}
if
(!father) {a[to].fa=root; add(root,to);}
}
q[++tail]=to;
}
}
return
;
}
void
dfs(
int
x)
{
f[x]=1;
int
p=st[x];
while
(p)
{
if
(!f[to[p]])
{
dfs(to[p]);
a[x].v+=a[to[p]].v;
}
p=nt[p];
}
}
int
main()
{
//freopen("1.in","r",stdin);
//freopen("my.out","w",stdout);
ios::sync_with_stdio(
false
);
cin>>n;
for
(
int
i=1;i<=n;i++) {cin>>ss; insert(i); s[i]=ss;}
getfail();
dfs(root);
for
(
int
i=1;i<=n;i++)
printf
(
"%d\n"
,a[num[i]].v);
return
0;
}