题意:给定n个将字符插至字符串尾的操作,求每次操作后本质不同的子串数。
题解: 一看“本质不同的子串”就知道是SAM啦,在建自动机时维护ans即可,注意数的值域过大要使用map。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read(){
int x=0,f=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
struct SAM{
map<int,int> trans;
int maxl,minl,fa;
}sam[200005];
int n,m,las=1,tot=1;
ll ans=0;
int insert(int c,int u){
int v=u,np=++tot;
sam[np].maxl=sam[v].maxl+1;
for (;v&&sam[v].trans[c]==0;v=sam[v].fa)
sam[v].trans[c]=np;
if (v==0){
sam[np].minl=sam[np].fa=1;
ans+=sam[np].maxl-sam[np].minl+1;
return np;
}
int x=sam[v].trans[c];
if (sam[x].maxl==sam[v].maxl+1){
sam[np].fa=x;
sam[np].minl=sam[x].maxl+1;
ans+=sam[np].maxl-sam[np].minl+1;
return np;
}
int y=++tot;
sam[y]=sam[x];
ans-=sam[x].maxl-sam[x].minl+1;
sam[y].maxl=sam[v].maxl+1;
sam[y].minl=sam[sam[y].fa].maxl+1;
sam[x].fa=sam[np].fa=y;
sam[x].minl=sam[np].minl=sam[y].maxl+1;
ans+=sam[x].maxl-sam[x].minl+1;
ans+=sam[np].maxl-sam[np].minl+1;
ans+=sam[y].maxl-sam[y].minl+1;
for (;v&&sam[v].trans[c]==x;v=sam[v].fa)
sam[v].trans[c]=y;
return np;
}
int main(){
n=read();
for (int i=1;i<=n;i++){
las=insert(read(),las);
printf("%lld\n",ans);
}
return 0;
}