题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2865
唯一出现的子串就是每个后缀除去和别的后缀最长的 LCP 之外的前缀;
所以用这个更新一段区间的答案,可以用线段树维护;
在 sa[i] ~ sa[i]+LCP+1 位置的答案由 LCP+1 更新,sa[i]+LCP+1 之后的位置就更新一个位置 ps 表示从 ps 到本位置的子串也可以;
最后两个取min即可;
注意如果整个后缀就是 LCP,那么就不能更新了;
线段树中 ps 的初值是0,取答案时注意判掉0。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #define mid ((l+r)>>1) using namespace std; int const xn=5e5+5,xm=(xn<<1); int n,m,rk[xn],sa[xn],tax[xn],tp[xn],ht[xn]; int cnt=1,ls[xm],rs[xm],len[xm],ps[xm]; char s[xn]; void Rsort() { for(int i=1;i<=m;i++)tax[i]=0; for(int i=1;i<=n;i++)tax[rk[tp[i]]]++; for(int i=1;i<=m;i++)tax[i]+=tax[i-1]; for(int i=n;i;i--)sa[tax[rk[tp[i]]]--]=tp[i]; } void work() { for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i; Rsort(); for(int k=1;k<=n;k<<=1) { int num=0; for(int i=n-k+1;i<=n;i++)tp[++num]=i; for(int i=1;i<=n;i++) if(sa[i]>k)tp[++num]=sa[i]-k; Rsort(); memcpy(tp,rk,sizeof rk); rk[sa[1]]=1; num=1; for(int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num; if(num==n)break; m=num; } } void get() { int k=0; for(int i=1;i<=n;i++) { if(rk[i]==1)continue; if(k)k--; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++; ht[rk[i]]=k; } } void build(int x,int l,int r) { len[x]=n+1; if(l==r)return; ls[x]=++cnt; build(ls[x],l,mid); rs[x]=++cnt; build(rs[x],mid+1,r); } void update(int x,int l,int r,int L,int R,int v) { if(l>=L&&r<=R){len[x]=min(len[x],v); return;} if(mid>=L)update(ls[x],l,mid,L,R,v); if(mid<R)update(rs[x],mid+1,r,L,R,v); } void chg(int x,int l,int r,int L,int R,int v) { if(l>=L&&r<=R){ps[x]=max(ps[x],v); return;} if(mid>=L)chg(ls[x],l,mid,L,R,v); if(mid<R)chg(rs[x],mid+1,r,L,R,v); } int query(int x,int l,int r,int pos,int v) { v=min(v,len[x]); if(l==r)return v; if(pos<=mid)return query(ls[x],l,mid,pos,v); else return query(rs[x],mid+1,r,pos,v); } int ask(int x,int l,int r,int pos,int v) { v=max(v,ps[x]); if(l==r)return v; if(pos<=mid)return ask(ls[x],l,mid,pos,v); else return ask(rs[x],mid+1,r,pos,v); } int main() { scanf("%s",s+1); n=strlen(s+1); m=125; work(); get(); build(1,1,n); for(int i=1;i<=n;i++) { int lcp=max(ht[i],ht[i+1]); if(sa[i]+lcp<=n)update(1,1,n,sa[i],sa[i]+lcp,lcp+1);// if(sa[i]+lcp+1<=n)chg(1,1,n,sa[i]+lcp+1,n,sa[i]); } for(int i=1;i<=n;i++) { int t=query(1,1,n,i,n+1),p=ask(1,1,n,i,0); if(p==0)printf("%d ",t);//! else printf("%d ",min(t,i-p+1)); } return 0; }