【ybtoj 高效进阶 2.5】 【AC自动机】 单词频率
题目
解题思路
先跑一遍AC自动机,记录一个sum[x],表示从x到根形成的字符串是多少个单词的前缀
再记录一个sum1[x],表示从x到根形成的字符串是多少单词的子串,是一个单词的前缀,一定也是它的子串,所以sum1[x]的初值是sum[x]
nxt[x]是失配后跳转到的点,从根到x形成的字符串一定包括从根到nxt[x]的字符串,所以sum1[nxt[x]]+=sum1[x] 因为nxt[x]是x的子串,所以x是那些单词的子串,nxt[x]也一定是
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1000010;
string s;
int n,tot,cnt;
int q[N],sum[N],ed[N],nxt[N],sum1[N],tr[N][26];
void add()
{
int l=s.size(),last=0;
for (int i=0;i<l;i++)
{
int c=s[i]-97;
if (!tr[last][c]) tr[last][c]=++tot;
last=tr[last][c];
sum[last]++;
}
ed[++cnt]=last; //标记单词结尾在哪个点
}
void bfs()
{
int h=0,t=0;
for (int i=0;i<26;i++)
if (tr[0][i]>0) nxt[tr[0][i]]=0,q[++t]=tr[0][i];
while (h<t) //处理出nxt
{
h++;
int x=q[h];
for (int i=0;i<26;i++)
{
if (tr[x][i]==0) tr[x][i]=tr[nxt[x]][i];
else {
q[++t]=tr[x][i];
nxt[q[t]]=tr[nxt[x]][i];
}
}
}
for (int i=1;i<=tot;i++) //赋初值
sum1[q[i]]=sum[q[i]];
for (int i=tot;i>0;i--)
sum1[nxt[q[i]]]+=sum1[q[i]];
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
cin>>s;
add();
}
bfs();
for (int i=1;i<=cnt;i++)
printf("%d\n",sum1[ed[i]]);
return 0;
}