1. 程式人生 > >bzoj 2865 字串識別——字尾陣列

bzoj 2865 字串識別——字尾陣列

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=2865

做出 ht[ ] 之後,sa[ ] 上每個位置和它前面與後面取 LCP ,其中較大的長度設為 d ,表示從 sa[ i ] 位置開始的子串的右端點要在 sa[ i ]+d-1 位置之後才是只出現了一次的。

那麼 sa[ i ] ~ sa[ i ]+d 位置的答案可以對 d+1 取 min ;至於 sa[ i ]+d+1 ~ n 位置,sa[ i ]可能成為它們答案的開頭位置,所以可以維護每個位置備選答案串開頭的最靠後位置(這樣最靠近自己),讓 sa[ i ]+d+1 ~ n 位置的這個值對 sa[ i ] 取 max 就行了。可以用線段樹維護。

注意那個“備選答案串開頭的最靠後位置”的初值不是0!不然第一個位置有可能和第0個位置組合了。應該是 -n 之類,才能排除影響。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
const int N=5e5+5,M=N<<1;
int n,sa[N],rk[N],tp[N],tx[N],ht[N];
int tot,Ls[M],Rs[M],tg1[M],tg2[M];
int ans[N]; char s[N]; int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} void Rsort(int n,int nm) { for(int i=1;i<=nm;i++)tx[i]=0; for(int i=1;i<=n;i++)tx[rk[i]]++; for(int i=2;i<=nm;i++)tx[i]+=tx[i-1]; for(int i=n;i;i--)sa[tx[rk[tp[i]]]--]=tp[i]; } void
get_sa(int n) { int nm=150; for(int i=1;i<=n;i++)tp[i]=i,rk[i]=s[i]; Rsort(n,nm); for(int k=1;k<=n;k<<=1) { int tot=0; for(int i=n-k+1;i<=n;i++)tp[++tot]=i; for(int i=1;i<=n;i++) if(sa[i]>k)tp[++tot]=sa[i]-k; Rsort(n,nm);memcpy(tp,rk,sizeof rk); nm=1;rk[sa[1]]=1; for(int i=2,u,v;i<=n;i++) { u=sa[i]+k;v=sa[i-1]+k;if(u>n)u=0;if(v>n)v=0; rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[u]==tp[v])?nm:++nm; } if(nm==n)break; } } void get_ht(int n) { for(int i=1,k=0,j;i<=n;i++) { for(k?k--:0,j=sa[rk[i]-1];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++); ht[rk[i]]=k;//rk[i]!!! } } void build(int l,int r,int cr) { tg1[cr]=n+1; tg2[cr]=-n;//// if(l==r)return; int mid=l+r>>1; ls=++tot; build(l,mid,ls); rs=++tot; build(mid+1,r,rs); } void mdfy(int l,int r,int cr,int L,int R,int k) { if(l>=L&&r<=R){tg1[cr]=Mn(tg1[cr],k);return;} int mid=l+r>>1; if(L<=mid)mdfy(l,mid,ls,L,R,k); if(mid<R)mdfy(mid+1,r,rs,L,R,k); } void mdfyx(int l,int r,int cr,int L,int R,int k) { if(l>=L&&r<=R){tg2[cr]=Mx(tg2[cr],k);return;} int mid=l+r>>1; if(L<=mid)mdfyx(l,mid,ls,L,R,k); if(mid<R)mdfyx(mid+1,r,rs,L,R,k); } void dfs(int l,int r,int cr,int lj1,int lj2) { lj1=Mn(lj1,tg1[cr]); lj2=Mx(lj2,tg2[cr]);//before l==r if(l==r){ans[l]=Mn(lj1,l-lj2+1);return;} int mid=l+r>>1; dfs(l,mid,ls,lj1,lj2); dfs(mid+1,r,rs,lj1,lj2); } int main() { scanf("%s",s+1);n=strlen(s+1); get_sa(n); get_ht(n); tot=1;build(1,n,1); for(int i=1,d;i<=n;i++) { d=sa[i]+Mx(ht[i],ht[i+1]); if(d>n)continue; mdfy(1,n,1,sa[i],d,d-sa[i]+1); if(d<n)mdfyx(1,n,1,d+1,n,sa[i]); } dfs(1,n,1,n+1,-n);//-n for(int i=1;i<=n;i++)printf("%d\n",ans[i]); return 0; }