建SAM,只有right集大小为1的节点对答案有贡献,
若其出现位置右端点为r,此节点可接受的最短串长为x,最长串长为y,
则对(r-x,r]用x更新最小值,对r-k (y<k≤x)则用k更新最小值
用两棵线段树维护答案,分别处理以上两种情况
#include<cstdio> #include<cstring> const int N=200005; int nx[N][26],fa[N],l[N],r[N],t[N],q[N],ql=0,qr=0,d[N],ptr=1,pv=1,len=0; char s[100005]; void ins(int w,int pos){ int p=pv,np=++ptr; t[np]=1; r[np]=pos; l[np]=l[p]+1; while(p&&!nx[p][w])nx[p][w]=np,p=fa[p]; if(!p)fa[np]=1; else{ int q=nx[p][w]; if(l[q]==l[p]+1)fa[np]=q; else{ int nq=++ptr; memcpy(nx[nq],nx[q],sizeof(nx[0])); l[nq]=l[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq; while(p&&nx[p][w]==q)nx[p][w]=nq,p=fa[p]; } } pv=np; } void build(){ for(int i=1;i<=ptr;i++)++d[fa[i]]; for(int i=1;i<=ptr;i++)if(!d[i])q[qr++]=i; while(ql!=qr){ int w=q[ql++]; int u=fa[w]; if(!u)continue; t[u]+=t[w]; r[u]=r[w]; if(!--d[u])q[qr++]=u; } } int mn[262144],mn2[262144]; inline void mins(int&a,int b){if(a>b)a=b;} void setmin(int l,int r,int x,int*mn){ for(l+=131073,r+=131075;l^r^1;l>>=1,r>>=1){ if(~l&1)mins(mn[l^1],x); if(r&1)mins(mn[r^1],x); } } int ans[100005]; int main(){ memset(mn,63,sizeof(mn)); memset(mn2,63,sizeof(mn2)); scanf("%s",s); for(len=0;s[len];++len)ins(s[len]-'a',len+1); build(); for(int i=2;i<=ptr;i++)if(t[i]==1){ int ml=l[fa[i]]; setmin(r[i]-ml+1,r[i],ml+1,mn); setmin(r[i]-l[i]+1,r[i]-ml,r[i]+1,mn2); } for(int i=1;i<131072;i++){ int lc=i<<1,rc=lc^1; mins(mn[lc],mn[i]),mins(mn[rc],mn[i]); mins(mn2[lc],mn2[i]),mins(mn2[rc],mn2[i]); } for(int i=1;i<=len;i++){ ans[i]=mn[i+131074]; mins(ans[i],mn2[i+131074]-i); } for(int i=1;i<=len;i++)printf("%d\n",ans[i]); return 0; }