回顾了一下Ac自动机,额,顺便重新背个版,原来那个这道题要被卡
这道题题意有点模糊,意思就是要你找到每一个单词出现的次数,其中他可以作为其他单词的连续字串出现例如样例中a自己本身出现了一次在aa中出现2次aaa中出现3次一共6次,同理aa本身出现一次在aaa中出现2次一共3次。
解法比较裸,根据刚才的题意,所以每一个单词只要他能作为另外一个单词的一段就能对答案贡献为一而在Ac自动机中的fail树叶恰好可以解决这个问题,每插入一个单词就在trie数将沿途路径节点的值++,最后沿着fail指正从下到上更新过去就好了。只可意会。。。
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 1000020
using namespace std;
int n,ch[maxn][26],sum[maxn],fail[maxn],sp[maxn],tot,q[maxn];
char s[maxn];
int insert(){
int len=strlen(s),rt=0;
for(int x,i=0;i<len;i++){
x=s[i]-'a';
if(!ch[rt][x])ch[rt][x]=++tot;
sum[rt=ch[rt][x]]++;
}
return rt;
}
void make(){
int l=0,r=1,u;
while(l<r){
u=q[l++];
for(int v,i=0;i<26;i++){
v=ch[u][i];
if(v)q[r++]=v,fail[v]=u==0?0:ch[fail[u]][i];
else ch[u][i]=u==0?0:ch[fail[u]][i];
}
}
for(int i=r-1;i;i--)sum[fail[q[i]]]+=sum[q[i]];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
sp[i]=insert();
}
make();
for(int i=1;i<=n;i++)printf("%d\n",sum[sp[i]]);
return 0;
}