题意:一个串初始为空,n次像串尾添加元素,每次添加后回答本质不同的子串个数
n<=1e5 字符集大小1e9
Sol:
本质不同的子串个数,考虑后缀数据结构
发现向结尾添加字符对后缀数组不友好,但是在开头添加很资瓷,相当于新添加一个后缀
于是考虑离线,把最终串搞出来在翻转一下,建立后缀数组,问题转化成动态添加后缀求不同子串个数
每次找到当前后缀应在位置,去掉后缀对前驱的影响,加上后继与前驱对她的影响,递推即可
找后缀可以用set
也可以再将这个过程逆转,每次删除后缀,这样可以用双线链表找前驱后继
算影响用正常的RMQ算法
因为字符集大需要先离散化
总复杂度O(nlogn)
好麻烦啊。。。年轻人,你听说过后缀自动机吗
会后缀自动机后发现这是一道裸题,字符集大?map一发
总复杂度也是O(nlogn) 还是在线的,比后缀数组高到不知道哪里去了
科技是第一生产力啊2333
Code:
后缀数组
//Suffix_Array - 528ms
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100009, Log = 18;
int c[maxn],t[maxn],t2[maxn],sa[maxn],rank[maxn],height[maxn];
int s[maxn],b[maxn],ft[maxn],nxt[maxn],pre[maxn];
int n;
long long ans[maxn];
struct ST
{
int mn[Log+1][maxn];
void build(int *a)
{
for(int i=1;i<=n;i++) mn[0][i]=a[i];
for(int i=1;i<=Log;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<i>>1)]);
}
int query(int l,int r)
{
int len=ft[r-l+1];
return min(mn[len][l],mn[len][r-(1<<len)+1]);
}
}H;
inline 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;
}
int find(int x)
{
int l=1,r=b[0];
while(l<=r)
{
int mid=(l+r)>>1;
if(b[mid]==x) return mid;
if(b[mid]<x) l=mid+1;else r=mid-1;
}
}
void build()
{
int mr=b[0],*x=t,*y=t2;
for(int i=1;i<=mr;i++) c[i]=0;
for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
for(int i=1;i<=mr;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=n-k+1;i<=n;i++) y[++p]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
for(int i=1;i<=mr;i++) c[i]=0;
for(int i=1;i<=n;i++) c[x[y[i]]]++;
for(int i=1;i<=mr;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i];
swap(x,y);
p=1;x[sa[1]]=1;
for(int i=2;i<=n;i++)
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) x[sa[i]]=p;
else x[sa[i]]=++p;
if(p>=n) break;
mr=p;
}
for(int i=1;i<=n;i++) rank[sa[i]]=i;
int h=0;
for(int i=1;i<=n;i++)
{
if(rank[i]==1) h=0;
else
{
int k=sa[rank[i]-1];
h--;if(h<0) h=0;
while(s[k+h]==s[i+h]) h++;
}
height[rank[i]]=h;
}
H.build(height);
for(int i=1;(1<<i)<=n;i++) ft[1<<i]=i;
for(int i=1;i<=n;i++) if(!ft[i]) ft[i]=ft[i-1];
for(int i=1;i<=n;i++) nxt[i]=i+1,pre[i]=i-1;
nxt[n]=pre[1]=0;
}
int lcp(int x,int y){return H.query(x+1,y);}
void erase(int x)
{
nxt[pre[x]]=nxt[x];
pre[nxt[x]]=pre[x];
nxt[x]=pre[x]=0;
}
int main()
{
n=read();
for(int i=1;i<=n;i++) b[i]=s[i]=read();
sort(b+1,b+1+n);b[0]=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) s[i]=find(s[i]);
reverse(s+1,s+1+n);
build();
for(int i=2;i<=n;i++) ans[1]-=height[i];
for(int i=2;i<=n;i++)
{
ans[i]=ans[i-1];
int x=rank[i-1],l=pre[x],r=nxt[x];
if(l) ans[i]+=lcp(l,x);
if(r) ans[i]+=lcp(x,r);
if(l&&r) ans[i]-=lcp(l,r);
erase(x);
}
for(int i=n;i>=1;i--) ans[n-i+1]+=1ll*(i+1)*i/2;
for(int i=n;i>=1;i--) printf("%lld\n",ans[i]);
return 0;
}
后缀自动机
//Suffix_Automaton - 764ms
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100009;
struct SAM
{
int tot,last,root;
long long ans;
struct state
{
map<int,int>son;
int mx,par;
}node[maxn<<1];
int calc(int x)
{
if(x==root) return 0;
return node[x].mx-node[node[x].par].mx;
}
void init(){tot=last=root=1;}
void extend(int x)
{
int p=last,np=++tot;
node[np].mx=node[p].mx+1;
while(p&&node[p].son[x]==0)
node[p].son[x]=np,p=node[p].par;
if(p==0)
{
node[np].par=root;
ans+=calc(np);
}
else
{
int q=node[p].son[x];
if(node[p].mx+1==node[q].mx)
{
node[np].par=q;
ans+=calc(np);
}
else
{
int nq=++tot;
node[nq].son=node[q].son;
node[nq].mx=node[p].mx+1;
ans-=calc(q);
node[nq].par=node[q].par;
node[np].par=node[q].par=nq;
ans+=calc(np)+calc(nq)+calc(q);
while(p&&node[p].son[x]==q)
node[p].son[x]=nq,p=node[p].par;
}
}
last=np;
}
}uuz;
int n;
inline 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;
}
int main()
{
n=read();uuz.init();
for(int i=1;i<=n;i++)
{
int x=read();
uuz.extend(x);
printf("%lld\n",uuz.ans);
}
return 0;
}