感觉我的做法好鬼畜啊。。。是不是错了啊
不过可爱的$asuldb$跟我做法一样诶$QwQ$
把所有串用特殊字符隔开,拼一块造$SAM$。
$endpos$集合就是子串出现位置,如果一个节点的$endpos$只在一个串里出现过,它就可以算进独特值里。
造$SAM$的时候可以给节点染个色,然后在$parent\ tree$上向上合并,如果某个节点只有一种颜色$x$,就可以给$x$产生贡献。
因为把所有串拼了起来,会有不是$x$的子串出现,就要记录一下最大的$endpos$值$ma$,显然$ma[i]=\max\{ma[j]\}(fa[j]=i)$,再记录一下每个串左端点的位置$ll[x]$,贡献就是$(ma[i]-len[fa[i]])-\max\{ma[i]-len[i]+1,ll[x]\}+1$
有可能该节点没有一个子串属于$x$,就会有负数,对$0$取个$\max$就好啦。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 200005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
#define son(x,y) son[x][y]
int h[maxn],col[maxn],ll[maxn],ma[maxn],son[maxn][27],fa[maxn],len[maxn],ans[maxn],last=1,cnt=1,all,num,n;
char s[maxn];
struct edge{
int pre,to;
}e[maxn];
inline void add(int from,int to){
e[++num].pre=h[from],h[from]=num,e[num].to=to;
}
inline void merge(int &x,int y){
if(x==y)return;
if(x==-1||y==-1||x&&y)x=-1;
else x=x|y;
}//合并颜色
void insert(int c,int i=0){
int p=last,ne=last=++cnt;
len[ne]=len[p]+1,ma[ne]=len[ne],col[ne]=i;//染色
while(p&&!son(p,c))son(p,c)=ne,p=fa[p];
if(!p)fa[ne]=1;
else {
int q=son(p,c);
if(len[q]==len[p]+1)fa[ne]=q;
else {
int sp=++cnt;
memcpy(son[sp],son[q],sizeof son[q]);
fa[sp]=fa[q],len[sp]=len[p]+1,fa[q]=fa[ne]=sp;
while(p&&son(p,c)==q)son(p,c)=sp,p=fa[p];
}
}
}
void dfs(int node=1){
int x;
for(register int i=h[node];i;i=e[i].pre)
x=e[i].to,dfs(x),merge(col[node],col[x]),ma[node]=max(ma[node],ma[x]);
x=col[node];
if(~x)ans[x]+=max((ma[node]-len[fa[node]])-max(ma[node]-len[node]+1,ll[x])+1,0);
}
int main(){
n=read();
int m,N=0;
for(register int i=1;i<=n;++i){
scanf("%s",s+1),m=strlen(s+1);
for(register int j=1;j<=m;++j)insert(s[j]-'a',i);
ll[i]=N+1,N+=m+1;//记录左端点
if(i!=n)insert(26);//特殊字符
}
for(register int i=2;i<=cnt;++i)add(fa[i],i);
dfs();
for(register int i=1;i<=n;++i)printf("%d\n",ans[i]);
}