Description
现在同学们把大多数作业都做完了,但是却被最后一个题给难住了。
一般地,对于一个字符串S,和S中第k个字符,定义子串T=S(i..j)为一个关于k的识别子串,当且仅当
1、i<=k<=j。
2、T在S中只出现一次。
比如,对于banana的第5个字符,“nana”,“anan”,“anana”,“nan”,“banan”和“banana”都是关于它的识别子串。
自然,识别子串越短越好(太长了也就失去意义了),现在请你计算出对于一个字符串S,关于S的每一位的最短识别子串分别有多长。
Input
一行,一个长度为L的字符串S,S只包含小写字母。
Output
L行,每行1个整数,第i行的数表示关于S的第i个元素的最短识别子串有多长。
Sample Input
agoodcookcooksgoodfood
Sample Output
1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4
Data Constraint
第一个点 L=100
第二个点 L=1000
第三个点 L=5000
第四个点到第十个点 L=100000
Solution
很容易看出是一道后缀自动机或者后缀数组的题。
我用的是后缀自动机:
在每次加入点处理的时候我们要处理出每个节点表示的状态到达起始点的最短长度。
这是表示当前节点在原串中能够取到的最短的子串。
处理出来我们暂且叫它mi[x],于是我们可以很自然地处理出每个节点状态出现的次数。
我们枚举一个节点表示的状态,如果这个状态出现的次数等于1,那么我们就可以使用线段树在当前节点在原串中的唯一right值p,在p-mi[x]+1~p上用mi[x]来取min。最后从后往前扫一遍再取min,详见代码。
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 200010
using namespace std;
char s[maxn];
int f[4*maxn],tag[4*maxn],len[maxn],las,sz,tot,n;
int last[maxn],next[maxn],tov[maxn],mi[maxn],ans[maxn];
int pre[maxn],son[maxn][27],freq[maxn],num[maxn];
void insert(int x,int y)
{
tov[++tot]=y;
next[tot]=last[x];
last[x]=tot;
}
void pick(int v,int z)
{
if(!v) return;
if(f[v]>z) f[v]=tag[v]=z;
}
void update(int x)
{
if(tag[x])
{
pick(x*2,tag[x]);
pick(x*2+1,tag[x]);
tag[x]=0;
}
}
void build(int l,int r,int v)
{
if(l==r){f[v]=n;return;}
int mid=(l+r)/2;
build(l,mid,v*2),build(mid+1,r,v*2+1);
f[v]=min(f[v*2],f[v*2+1]);
}
void change(int l,int r,int v,int x,int y,int z)
{
if(l==x&&r==y)
{
pick(v,z);
return;
}
update(v);
int mid=(l+r)/2;
if(y<=mid) change(l,mid,v*2,x,y,z);
else if(x>mid) change(mid+1,r,v*2+1,x,y,z);
else change(l,mid,v*2,x,mid,z),change(mid+1,r,v*2+1,mid+1,y,z);
}
void find1(int l,int r,int v)
{
if(l==r){ans[l]=f[v];return;}
update(v);
int mid=(l+r)/2;
find1(l,mid,v*2);
find1(mid+1,r,v*2+1);
}
void add(int i,int x)
{
int now=++sz,p=las;mi[now]=len[now]=len[las]+1;
for (;p&&!son[p][x];p=pre[p])
son[p][x]=now,mi[now]=min(mi[now],mi[p]+1);
if(p)
{
int q=son[p][x];
if(len[p]+1==len[q]) pre[now]=q;
else
{
int np=++sz;len[np]=len[p]+1;
memcpy(son[np],son[q],sizeof(son[q]));
mi[np]=mi[q],mi[q]=len[p]+2,num[np]=num[q];
for(;son[p][x]==q;p=pre[p]) son[p][x]=np;
pre[np]=pre[q],pre[q]=pre[now]=np;
}
}
else pre[now]=1;
num[now]=i,freq[now]=1;
las=now;
}
void dfs(int x)
{
for (int i=last[x];i;i=next[i])
dfs(tov[i]),freq[x]+=freq[tov[i]];
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);int i,j;sz=las=1;
for (i=1;i<=n;++i)
add(i,s[i]-96);
for (i=2;i<=sz;++i)
insert(pre[i],i);
dfs(1);
build(1,n,1);
for (i=2;i<=sz;++i)
if(num[i]&&freq[i]==1) change(1,n,1,num[i]-mi[i]+1,num[i],mi[i]);
find1(1,n,1);
for (i=n-1;i>=1;--i) ans[i]=min(ans[i],ans[i+1]+1);
for (i=1;i<=n;++i) printf("%d\n",ans[i]);
}