bzoj1396. 识别子串
题目大意:给定一个字符串,求包含第i个字母且出现次数唯一的串的最小长度。
可以先考虑以每个字母为结尾且出现次数唯一的串会怎么分布。
由
p
a
r
e
n
t
parent
parent树的性质可以看出,这个串的长度区间一定为
[
l
e
n
[
f
a
i
]
+
1
,
i
]
[len[fa_i]+1,i]
[len[fai]+1,i]。
因此,区间
[
i
−
l
e
n
[
f
a
i
]
,
i
]
[i-len[fa_i],i]
[i−len[fai],i]的字母都可以被这个等价类中最短的串覆盖,也就是长度为
l
e
n
[
f
a
i
]
+
1
len[fa_i]+1
len[fai]+1的串覆盖,这个贡献可以用线段树标记永久化来维护。
而对左边的
[
1
,
l
e
n
[
f
a
i
]
−
1
]
[1,len[fa_i]-1]
[1,len[fai]−1]这个区间的贡献为
l
−
i
+
1
l-i+1
l−i+1其中
l
l
l为左端点,如果
l
l
l固定,那么
i
i
i越小答案一定最优,所以只需要考虑最近的
i
i
i即可,这个贡献可以通过双指针来完成。
#include<bits/stdc++.h>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 200005
#define int long long
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
struct edge{
int next,to;
};
struct SAM{
int id[maxn],pos[maxn],tot=1,lt=1,num,l[maxn],ch[maxn][26],sz[maxn],f[maxn],last[maxn];
edge g[maxn];
void insert(int c,int i){
int v=++tot,u=lt;lt=tot;pos[tot]=i;id[i]=tot;
sz[v]=1;l[v]=l[u]+1;
while(u&&!ch[u][c]) {ch[u][c]=v;u=f[u];}
if(!u) {f[v]=1;return;}
int x=ch[u][c];
if(l[x]==l[u]+1) {f[v]=x;return;}
int y=++tot;pos[y]=pos[x];
l[y]=l[u]+1;f[y]=f[x];f[x]=f[v]=y;
memcpy(ch[y],ch[x],sizeof(ch[x]));
while(u&&ch[u][c]==x) {ch[u][c]=y;u=f[u];}
}
void add(int from,int to)
{
g[++num].next=last[from];
g[num].to=to;
last[from]=num;
}
void dfs(int x)
{
for(int i=last[x];i;i=g[i].next)
{
int v=g[i].to;
dfs(v);
sz[x]+=sz[v];
}
}
}sam;
struct ST{
int tag[maxn<<2],a[maxn];
void init(){
memset(tag,0x3f,sizeof(tag));
}
void modify(int k,int l,int r,int x,int y,int val)
{
if(x>y) return;
if(x<=l&&r<=y)
{
tag[k]=min(tag[k],val);
return;
}
int mid=(l+r)>>1;
if(x<=mid) modify(k<<1,l,mid,x,y,val);
if(mid+1<=y) modify(k<<1|1,mid+1,r,x,y,val);
}
int query(int k,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return tag[k];
int mid=(l+r)>>1,ans=tag[k];
if(x<=mid) ans=min(ans,query(k<<1,l,mid,x,y));
if(mid+1<=y) ans=min(ans,query(k<<1|1,mid+1,r,x,y));
return ans;
}
}st;
char a[maxn];
int f[maxn];
signed main()
{
cin>>a+1;
memset(f,0x3f,sizeof(f));
int len=strlen(a+1);
for(int i=1;i<=len;i++) sam.insert(a[i]-'a',i);
for(int i=2;i<=sam.tot;i++) sam.add(sam.f[i],i);
sam.dfs(1);st.init();
int last=1;
for(int i=1;i<=len;i++){
int u=sam.id[i];
if(sam.sz[u]>1) continue;
f[i]=min(f[i],sam.l[sam.f[u]]+1);
int r=i-sam.l[sam.f[u]];
st.modify(1,1,len,r+1,i-1,sam.l[sam.f[u]]+1);
while(last<=r){
f[last]=min(f[last],i-last+1);
last++;
}
}
for(int i=1;i<=len;i++){
f[i]=min(f[i],st.query(1,1,len,i,i));
printf("%d\n",f[i]);
}
return 0;
}