BZOJ1396 识别子串
题目大意
给定一个字符串,求对于每一个位置,求包含它的只出现一次的子串的最短长度。
题解
先建后缀自动机,对于每一个$|Endpos|=1$的节点,设其右端点为$r$。
对于所有$x\in [r-minlen+1,r]$,可以用$minlen$的长度更新答案。
对于所有$x\in [r-maxlen+1,r-minlen]$,可以用$r-x$的长度更新。
用线段树维护两个标记,一个用来更新能够覆盖答案的最小长度$minlen$,即情况一,另一个用来更新能够用来更新答案的最左的右端点$r$,即情况二。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 200020 using namespace std; namespace IO{ const int BS=(1<<20); int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return *HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; Putchar('\n'); } int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; char S[M]; int n; namespace segtree{ int tg[M<<1][2]; void init(){memset(tg,0x3f,sizeof(tg));} void mdf(int x,int l,int r,int L,int R,int dt,int kd){ if(L<=l&&r<=R){tg[x][kd]=min(dt,tg[x][kd]);return;} if(r<L||R<l) return; int mid=((l+r)>>1); mdf(x<<1,l,mid,L,R,dt,kd),mdf(x<<1|1,mid+1,r,L,R,dt,kd); } void qry(int x,int l,int r,int ps,int len){ ps=min(ps,tg[x][0]),len=min(len,tg[x][1]); if(l==r){write(min(ps-l+1,len));return;} int mid=((l+r)>>1); qry(x<<1,l,mid,ps,len),qry(x<<1|1,mid+1,r,ps,len); } } namespace SAM{ int len[M],rs[M],fa[M],t[M][26],F[M],C[M],T[M]; int now,cnt,ed,vs[M],sz[M],ps[M]; void init(){cnt=ed=1,now=0,memset(rs,-1,sizeof(rs));} void ins(int c){ int x=ed; len[ed=++cnt]=++now,rs[ed]=now,sz[ed]=1; while(x&&!t[x][c]) t[x][c]=ed,x=fa[x]; if(!x){fa[ed]=1;return;} int y=t[x][c]; if(len[y]==len[x]+1){fa[ed]=y;return;} len[++cnt]=len[x]+1,fa[cnt]=fa[y],fa[y]=fa[ed]=cnt; memcpy(t[cnt],t[y],sizeof(t[y])); while(x&&t[x][c]==y) t[x][c]=cnt,x=fa[x]; } void build(){ for(int i=1;i<=cnt;i++) C[len[i]]++; for(int i=1;i<=n;i++) C[i]+=C[i-1]; for(int i=1;i<=cnt;i++) T[C[len[i]]--]=i; for(int i=cnt;i>0;i--){ int x=T[i]; sz[fa[x]]+=sz[x]; if(sz[x]) rs[fa[x]]=rs[x]; if(sz[x]>1||!sz[x]) continue; segtree::mdf(1,1,n,rs[x]-len[fa[x]],rs[x],len[fa[x]]+1,1); segtree::mdf(1,1,n,rs[x]-len[x]+1,rs[x]-len[fa[x]]-1,rs[x],0); } } } int main(){ scanf("%s",S+1),n=strlen(S+1),SAM::init(); for(int i=1;i<=n;i++) SAM::ins(S[i]-'a'); segtree::init(),SAM::build(); segtree::qry(1,1,n,M,M),flush();return 0; }