BZOJ 2865(後綴數組+線段樹)
阿新 • • 發佈:2019-01-04
sin 數組 我們 無法 ios max eight char s void
我們無法加上\(max(height[rk[i]],height[rk[i]+1])+1\)最後的那個1。
很容易想到只考慮後綴長度必須為\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出現過一次)然後我正著做一遍反著做一遍,再取一個\(min\)最後掛了。。。
設\(x=max(height[rk[i]],height[rk[i]+1])+1\)我們考慮\(i\)的貢獻,會給區間\([i,i+x-1]\)一個貢獻x
,設\(r=i+x-1\)然後會給r+1一個貢獻x+1就是(r+1)-i+1,接著是r+2的貢獻(r+2)-i+1。。。
最後我們對每一個點求出這個點的最小的貢獻。這堆東西可以用線段樹維護。
值得註意的一點是當i+x-1>n時並不能產生貢獻,因為此時已經到了字符串末尾。
#include<iostream> #include<cstring> #include<cmath> #include<cstdio> #include<algorithm> using namespace std; #define mid ((l+r)>>1) #define ls now<<1 #define rs now<<1|1 const int N=501000; int c[N],x[N],y[N],sa[N],rk[N],height[N],n,m; int lazy1[N*5],lazy2[N*5],mn[N*5]; char s[N]; void get_sa(){ for(int i=1;i<=n;i++)c[x[i]=s[i]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1){ int num=0; for(int i=n-k+1;i<=n;i++)y[++num]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k; for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[i]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0; for(int i=1;i<=n;i++)swap(x[i],y[i]); x[sa[1]]=1;num=1; for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num; if(n==num)break; m=num; } } void get_height(){ int k=0; for(int i=1;i<=n;i++)rk[sa[i]]=i; for(int i=1;i<=n;i++){ if(rk[i]==1)continue; if(k)k--; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++; height[rk[i]]=k; } } void build(int l,int r,int now){ if(l==r){mn[now]=n;return;} build(l,mid,ls); build(mid+1,r,rs); } void update(int now){ mn[now]=min(mn[ls],mn[rs]); } void pushdown(int l,int r,int now){ if(l==r)return; if(lazy1[now]){ mn[ls]=min(mn[ls],lazy1[now]); mn[rs]=min(mn[rs],lazy1[now]); if(lazy1[rs])lazy1[rs]=min(lazy1[rs],lazy1[now]); else lazy1[rs]=lazy1[now]; if(lazy1[ls])lazy1[ls]=min(lazy1[ls],lazy1[now]); else lazy1[ls]=lazy1[now]; lazy1[now]=0; } if(lazy2[now]){ mn[ls]=min(mn[ls],l+lazy2[now]); mn[rs]=min(mn[rs],mid+1+lazy2[now]); if(lazy2[rs])lazy2[rs]=min(lazy2[rs],lazy2[now]); else lazy2[rs]=lazy2[now]; if(lazy2[ls])lazy2[ls]=min(lazy2[ls],lazy2[now]); else lazy2[ls]=lazy2[now]; lazy2[now]=0; } } void add1(int l,int r,int L,int R,int w,int now){ pushdown(l,r,now); if(l==L&&r==R){ lazy1[now]=w; mn[now]=min(mn[now],w); return; } if(L>mid)add1(mid+1,r,L,R,w,rs); else if(R<=mid)add1(l,mid,L,R,w,ls); else add1(l,mid,L,mid,w,ls),add1(mid+1,r,mid+1,R,w,rs); update(now); } void add2(int l,int r,int L,int R,int w,int now){ if(L>R)return; pushdown(l,r,now); if(l==L&&r==R){ lazy2[now]=w; mn[now]=min(l+w,mn[now]); return; } if(L>mid)add2(mid+1,r,L,R,w,rs); else if(R<=mid)add2(l,mid,L,R,w,ls); else add2(l,mid,L,mid,w,ls),add2(mid+1,r,mid+1,R,w,rs); update(now); } int check(int l,int r,int x,int now){ pushdown(l,r,now); if(l==r)return mn[now]; if(x>mid)return check(mid+1,r,x,rs); else return check(l,mid,x,ls); } int main(){ scanf("%s",s+1); n=strlen(s+1); m=122; get_sa();get_height(); build(1,n,1); for(int i=1;i<=n;i++){ int tmp=max(height[rk[i]],height[rk[i]+1])+1; if(i+tmp-1<=n)add1(1,n,i,i+tmp-1,tmp,1); add2(1,n,i+tmp,n,-i+1,1); } for(int i=1;i<=n;i++)printf("%d\n",check(1,n,i,1)); return 0; }
BZOJ 2865(後綴數組+線段樹)