【題解】bzoj4310跳蚤(SA)

【題解】bzoj4310跳蚤(SA)

cao 還有二分字典序這種操作。。。。

問題性質顯然滿足二分性,但是我們只能對數進行二分,但是如果可以根據一個數確定一個字符串就好了。

用SA可以實現,根據一個整數x,可以在\(O(\min (n,ans))\),得到字典序在所有子串中x位的那個串具體是什麼。

二分完之後就是貪心了,從後往前枚舉每個位置要不要斷開,現在問題就變成了一個母串中有兩個子串要比較字典序,也是SA+倍增就行了。

貪心時可以發現,直接在當前位置的後面斷開最優,因爲這樣分出來的字典序更小。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;

void Suf_sort(char*str,int*rk,int*sa,int*h,int n){
    static int temp[maxn<<1],b[maxn];
    for(int k=0,m=127;(1<<k>>1)<=n;++k){
        if(!k) for(int t=1;t<=n;++t) temp[t]=t,rk[t]=str[t],temp[t+n]=0;
        int l=1<<k>>1,p=0,q=l;
        for(int t=1;t<=n;++t){
            if(sa[t]>n-l) temp[++p]=sa[t];
            if(sa[t]>l) temp[++q]=sa[t]-l;
        }
        for(int t=1;t<=n;++t) ++b[rk[t]];
        for(int t=1;t<=m;++t) b[t]+=b[t-1];
        for(int t=n;t;--t) sa[b[rk[temp[t]]]--]=temp[t];
        memset(b+1,0,m<<2); memcpy(temp+1,rk+1,n<<2);
        rk[sa[1]]=1;
        for(int t=2;t<=n;++t)
            rk[sa[t]]=temp[sa[t]]==temp[sa[t-1]]&&temp[sa[t]+l]==temp[sa[t-1]+l]?rk[sa[t-1]]:rk[sa[t-1]]+1;
        m=rk[sa[n]];
    }
    for(int t=1,p=0;t<=n;++t){
        if(p) --p;
        if(rk[t]==1) p=0;
        else while(str[t+p]==str[sa[rk[t]-1]+p]) ++p;
        h[rk[t]]=p;//here
    }
}

int rk[maxn],sa[maxn],st[21][maxn],lg[maxn],n,k;
char c[maxn];
pair<int,int> getKth(ll k){
    for(int t=1;t<=n;++t)
        if(k>n-sa[t]-st[0][t]+1) k-=n-sa[t]-st[0][t]+1;
        else return make_pair(sa[t],sa[t]+k+st[0][t]-1);
    return make_pair(-1,-1);
}

int _que(int l,int r){return min(st[lg[r-l+1]][l],st[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);}
int lcp(int p,int q){if(rk[p]>rk[q])swap(p,q);return p==q?n-p+1:_que(rk[p]+1,rk[q]);}

bool isNotMore(pair<int,int> a,pair<int,int> b){//<=
    int lena=a.second-a.first+1,lenb=b.second-b.first+1;
    int p=min(min(lena,lenb),lcp(a.first,b.first));
    if(p==lena) return 1;
    if(p==lenb) return 0;
    return c[a.first+p]<=c[b.first+p];
}

bool chek(ll mid){
    pair<int,int> temp=getKth(mid);
    int ret=0;
    for(int t=n,r=n;t;--t){
        if(c[t]>c[temp.first]) return 0;
        if(!isNotMore(make_pair(t,r),temp)) r=t,++ret;
        if(ret>k) return 0;
    }
    return ret<=k;
}

int main(){
    scanf("%d%s",&k,c+1); n=strlen(c+1); --k;
    for(int t=2;t<=n;++t) lg[t]=lg[t>>1]+1;
    Suf_sort(c,rk,sa,st[0],n);
    for(int t=1;t<=lg[n];++t)
        for(int i=1;i<=n;++i)
            st[t][i]=min(st[t-1][i],st[t-1][i+(1<<t>>1)]);//here!!!!
    ll tol=(n+1ll)*n/2;
    for(int t=1;t<=n;++t) tol-=st[0][t];
    ll l=1,r=tol,mid;
    do
        if(!chek(mid=(l+r)>>1)) l=mid+1;
        else r=mid-1;
    while(l<=r);
    pair<int,int> s=getKth(r+1);
    cout<<string(c+s.first,c+s.second+1)<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章