您的位置:首页 > 其它

bzoj 1396: 识别子串

2018-03-19 20:54 316 查看

题意:

给出一个字符串,对于每一个位置,求出最短的包含它的只在串中出现一次的子串长度。

题解:

建出sam,然后处理right集合为1的串。

然后线段树维护两个东西,一个以它为左端点,最靠左的合法右端点。

一个是直接包含的答案。

code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=1<<28;
char s[100010];
int n;
struct SAM{
int a[26],max,par;
}sam[200010];int tot,tail,root,ri[200010];
struct node{
int y,next;
}a[200010];int len=0,last[200010];
struct trnode{
int lc,rc,c1,c2;
}tr[200010];int trtot=0;
void ins(int x,int y)
{
a[++len].y=y;
a[len].next=last[x];last[x]=len;
}
void addsam(int c,int len)
{
int np=++tot,p=tail;
sam[np].max=len;ri[np]=len;
for(;p&&!sam[p].a[c];p=sam[p].par) sam[p].a[c]=np;
tail=np;
if(!p) sam[np].par=root;
else
{
int q=sam[p].a[c];
if(sam[q].max==sam[p].max+1) sam[np].par=q;
else
{
int nq=++tot;sam[nq]=sam[q];
sam[nq].max=sam[p].max+1;
sam[np].par=sam[q].par=nq;
for(;p&&sam[p].a[c]==q;p=sam[p].par) sam[p].a[c]=nq;
}
}
}
int ans[100010];
void change(int x,int l,int r,int fl,int fr,int c,int op)
{
if(l==fl&&r==fr)
{
if(op==1) tr[x].c1=min(tr[x].c1,c);
else tr[x].c2=min(tr[x].c2,c);
return;
}
int mid=(l+r)/2;
if(fr<=mid) change(tr[x].lc,l,mid,fl,fr,c,op);
else if(fl>mid) change(tr[x].rc,mid+1,r,fl,fr,c,op);
else change(tr[x].lc,l,mid,fl,mid,c,op),change(tr[x].rc,mid+1,r,mid+1,fr,c,op);
}
void dfs(int x)
{
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;dfs(y);
if(!ri[x]) ri[x]=ri[y];
else ri[x]=(ri[x]==ri[y])?ri[y]:-1;
}
if(ri[x]!=-1)
{
int l=ri[x]-sam[x].max+1,r=ri[x],k=r-sam[sam[x].par].max;
change(1,1,n,k,r,(r-k+1),1);change(1,1,n,l,k,r,2);
}
}
int bt(int l,int r)
{
int x=++trtot;tr[x].c1=tr[x].c2=inf;
if(l!=r)
{
int mid=(l+r)/2;
tr[x].lc=bt(l,mid);
tr[x].rc=bt(mid+1,r);
}
return x;
}
void findans(int x,int l,int r)
{
if(l==r)
{
ans[l]=min(tr[x].c1,tr[x].c2-l+1);
return;
}
int lc=tr[x].lc,rc=tr[x].rc,mid=(l+r)/2;
tr[lc].c1=min(tr[lc].c1,tr[x].c1);
tr[rc].c1=min(tr[rc].c1,tr[x].c1);
tr[lc].c2=min(tr[lc].c2,tr[x].c2);
tr[rc].c2=min(tr[rc].c2,tr[x].c2);
findans(lc,l,mid);findans(rc,mid+1,r);
}
int main()
{
root=tot=tail=1;
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++) addsam(s[i]-'a',i);
for(int i=2;i<=tot;i++) ins(sam[i].par,i);
bt(1,n);dfs(1);
findans(1,1,n);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: