对所有字符串构建一颗广义后缀自动机.
构建过程总,如果转移边已经存在,则说明转移到的字符串在之前出现过,所以该字串的所有字串也都出现过.
那么,就对该点打上标记,对整个后缀树进行一边深度优先搜索.
更新答案即可.
Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 1000000
#define N 30
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int idx[maxn],n,m,ans[maxn];
char str[maxn];
struct Edge{
int cnt;
int head[maxn], to[maxn], nex[maxn];
void addedge(int u,int v) { nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v; }
void dfs(int u) {
for(int i=head[u];i;i=nex[i]) dfs(to[i]);
for(int v=head[u];v;v=nex[v]) {
if(idx[to[v]]==-1){idx[u]=-1;break;}
if(!idx[to[v]])continue;
if(!idx[u])idx[u]=idx[to[v]];
else if(idx[u]!=idx[to[v]]){idx[u]=-1;break;}
}
}
}G;
struct EXSAM{
int last,tot;
int ch[maxn][N],f[maxn],len[maxn];
void init() { last=++tot; }
void ins(int c,int kth){
int p=last,np,nq;
if(ch[p][c]){
int q=ch[p][c];
if(len[q]==len[p]+1) last=q,idx[q]=-1;
else {
nq=++tot,last=nq; idx[nq]=-1;
f[nq]=f[q],f[q]=nq,len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q) idx[p]=-1,ch[p][c]=nq,p=f[p];
}
}
else {
np=++tot,last=np,len[np]=len[p]+1;idx[np]=kth;
while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
if(!p) f[np]=1;
else {
int q=ch[p][c];
if(len[q]==len[p]+1) f[np]=q;
else {
nq=++tot;
f[nq]=f[q],f[q]=f[np]=nq,len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
}
}
}
}
void Solve() {
for(int i=2;i<=tot;++i) G.addedge(f[i],i);
G.dfs(1);
for(int i=1;i<=tot;++i) {
int t=idx[i];
if(t!=-1) ans[t]+=len[i]-len[f[i]];
}
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
}
}T;
int main(){
//etIO("input");
scanf("%d",&n),T.init();
for(int i=1;i<=n;++i) {
scanf("%s",str),m=strlen(str),T.last=1;
for(int j=0;j<m;++j) T.ins(str[j]-'a',i);
}
T.Solve();
return 0;
}