题意:一个串初始为空,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; }