题意
内存限制:128 MiB
时间限制:1000 ms
L
≤
100000
L \leq 100000
L≤100000
题解
考虑建出parent树,只有叶子结点上所接受的串在原串中出现一次,而其最长接受的串为原串的一段前缀
所以对于其叶子结点
i
i
i 上在原串中对应着
[
1
,
l
e
n
i
]
[1,len_i]
[1,leni]~
[
l
e
n
i
−
l
e
n
f
a
i
,
l
e
n
i
]
[len_i-len_{fa_i},len_i]
[leni−lenfai,leni]
所以对于
[
1
,
l
e
n
i
−
l
e
n
f
a
i
]
[1,len_i-len_{fa_i}]
[1,leni−lenfai] 的点,可以把
l
e
n
i
len_i
leni 作为其右端点,对于
[
l
e
n
i
−
l
e
n
f
a
i
+
1
,
l
e
n
i
]
[len_i-len_{fa_i}+1,len_i]
[leni−lenfai+1,leni] 的点,可以把
l
e
n
f
a
i
+
1
len_{fa_i}+1
lenfai+1 作为包含它的区间长度
所以开两棵线段树,维护最小右端点和最小长度即可
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,lst=1,sz=1,ans[N];
char s[N];bool g[N];
struct SAM{
int link,len;
map<int,int>nx;
}a[N];
void build(int x){
int np=++sz,p=lst;
a[np].len=a[p].len+1;
while(p && !a[p].nx.count(x))
a[p].nx[x]=np,p=a[p].link;
if (!p) a[np].link=1;
else{
int q=a[p].nx[x];
if (a[q].len==a[p].len+1)
a[np].link=q;
else{
int nq=++sz;
a[nq].len=a[p].len+1;
a[nq].link=a[q].link;
a[nq].nx=a[q].nx;
a[q].link=a[np].link=nq;
while(p && a[p].nx[x]==q)
a[p].nx[x]=nq,p=a[p].link;
}
}
lst=np;
}
struct T{
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
int in[N*2];
void build(int k,int l,int r){
in[k]=1e9;if (l==r) return;
build(Ls,l,mid);build(Rs,mid+1,r);
}
void update(int k,int l,int r,int L,int R,int v){
if (L>R) return;
if (L<=l && r<=R){in[k]=min(in[k],v);return;}
if (mid>=L) update(Ls,l,mid,L,R,v);
if (mid<R) update(Rs,mid+1,r,L,R,v);
}
int query(int k,int l,int r,int x,int v){
if (l==r) return min(v,in[k]);
if (mid>=x) return query(Ls,l,mid,x,min(v,in[k]));
return query(Rs,mid+1,r,x,min(v,in[k]));
}
}t[2];
int main(){
scanf("%s",s+1);n=strlen(s+1);
for (int i=1;i<=n;i++) build(s[i]-'a');
for (int i=1;i<=sz;i++) g[a[i].link]=1;
t[0].build(1,1,n);t[1].build(1,1,n);
for (int l,r,i=1;i<=sz;i++) if (!g[i])
r=l=a[i].len,l-=a[a[i].link].len,
t[0].update(1,1,n,1,l-1,r),
t[1].update(1,1,n,l,r,r-l+1);
for (int i=1;i<=n;i++)
printf("%d\n",min(t[0].query(1,1,n,i,1e9)-i+1,t[1].query(1,1,n,i,1e9)));
return 0;
}