1. 程式人生 > >BZOJ 2865(後綴數組+線段樹)

BZOJ 2865(後綴數組+線段樹)

sin 數組 我們 無法 ios max eight char s void

很容易想到只考慮後綴長度必須為\(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時並不能產生貢獻,因為此時已經到了字符串末尾。

我們無法加上\(max(height[rk[i]],height[rk[i]+1])+1\)最後的那個1。

#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(後綴數組+線段樹)