题意:给一个字符串,对于每个位置i,求出最短满足的子串[l,r]使得i∈[l,r]且这个子串只出现一次,输出这个子串的长度
神(shui)题,AC后看了下别人的code发现全都是后缀数组(难看)
可能是因为我太弱了所以只想到SAM的算法
做法:建立SAM,求出parent树,预处理倍增,让后就可以O(lg n)查询一个字串的出现次数了
接下来,我们对于每个l∈[1,n],我们求出一个最小的r使得[l,r]在整个串里面只出现一次
那么对于每个点i,ans[i]有两种情况:
1.i被一个最短的区间[lj,rj]覆盖,那么我们可以
把这些l,r区间按长度从大到小排序,并且用线段树区间覆盖
2.有可能是一个区间[lj,rj](rj<i)的延伸[lj,i]
对于这种情况,我们把所有区间按r排序,对于i,我们用二分找到最大的j使得rj<i,让后答案就是i-max(l[k])(k<=j)
两种情况取较小就可以了
代码很长很丑,跑的也很慢。。。。
#include<stdio.h> #include<string.h> #include<algorithm> #define N 1000010 #define f F[0] using namespace std; struct Pr{ int l,r; } R[N]; int F[20][N],s[N][26],mx[N],sz[N],d[N],M[N],n; int v[N],r[N],m=1,cnt=1,last=1,A[N],pos[N],T[N<<2]; char str[N]; inline bool c1(Pr a,Pr b){ return a.r==b.r?a.l>b.l:a.r<b.r; } inline bool c2(Pr a,Pr b){ return a.r-a.l>b.r-b.l; } inline int extend(char c,int& o){ int p=last,np=last=o=++cnt,q,nq; c-='a'; mx[np]=mx[p]+1; sz[np]=1; for(;p&&!s[p][c];p=f[p]) s[p][c]=np; if(!p) return f[np]=1; q=s[p][c]; if(mx[p]+1==mx[q]) f[np]=q; else { nq=++cnt; mx[nq]=mx[p]+1; f[nq]=f[q]; f[q]=f[np]=nq; memcpy(s[nq],s[q],26<<2); for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq; } } inline void build(){ for(int i=1;i<=cnt;++i) ++v[mx[i]]; for(int i=1;i<=n;++i) v[i]+=v[i-1]; for(int i=cnt;i;--i) r[v[mx[i]]--]=i; for(int p,i=cnt;i;--i) sz[f[r[i]]]+=sz[r[i]]; for(int p,i=1;i<=cnt;++i){ p=r[i]; d[p]=d[f[p]]+1; for(int j=1;j<20;++j) F[j][i]=F[j-1][F[j-1][i]]; } } int gSz(int l,int r){ if(r>n) return 1; int x=pos[r]; for(int p,i=19;~i&&sz[x]<=1;--i){ p=F[i][x]; mx[p]>=r-l+1?x=p:0; } return sz[x]; } void cover(int l,int r,int x,int L,int R,int k){ if(L<=l && r<=R){ T[x]=k; return; } if(T[x]){ T[x<<1]=T[x<<1|1]=T[x]; T[x]=0; } int mid=l+r>>1; if(L<=mid) cover(l,mid,x<<1,L,R,k); if(mid<R) cover(mid+1,r,x<<1|1,L,R,k); } int gCover(int l,int r,int x,int p){ if(l==r) return T[x]; if(T[x]){ T[x<<1]=T[x<<1|1]=T[x]; T[x]=0; } int mid=l+r>>1; if(p<=mid) return gCover(l,mid,x<<1,p); else return gCover(mid+1,r,x<<1|1,p); } int main(){ scanf("%s",str+1); n=strlen(str+1); for(int i=1;i<=n;++i) extend(str[i],pos[i]); build(); int l=1,r=1; for(;l<=n;){ for(;r<=n&&gSz(l,r)>1;++r); R[l]=(Pr){l,r}; if(++l>r) ++r; } sort(R+1,R+1+n,c2); T[1]=n; for(int i=1;i<=n;++i) if(R[i].r<=n) cover(1,n,1,R[i].l,R[i].r,R[i].r-R[i].l+1); for(int i=1;i<=n;++i) A[i]=gCover(1,n,1,i); sort(R+1,R+1+n,c1); M[0]=-n; for(int i=1;i<=n;++i) M[i]=max(M[i-1],R[i].l); for(int i=1;i<=n;++i){ l=0; r=n; for(int mid;l<r;){ mid=l+r+1>>1; if(R[mid].r<=i) l=mid; else r=mid-1; } printf("%d ",min(A[i],i-M[l]+1)); } }