BZOJ 4310 跳蚤

首先我们知道我们要求的是使得最大值最小,显然是要二分的

我们先对原串建出后缀自动机

之后二分答案是第k小的字符串

对于答案可行性的判定:

我们注意到对于每一个区间,其字典序最大的子串一定是区间的某个后缀

那么我们不妨从后往前扫,这样每次只会增加一个后缀

我们只需要判断这个后缀是否比当前答案小就可以了

如果比当前答案大,就划分出一组

可以证明,这样分组是在满足条件的情况下分组最少的

至于两个串比较大小,可以用哈希做到O(logn)

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
 
typedef unsigned long long LL;
const int maxn=400010;
const int x=13331;
int k,n,clen;
char s[maxn];
char c[maxn];
bool vis[maxn];
LL dp[maxn];
LL xp[maxn],h[maxn],H[maxn];
 
int cnt=0,la=0;
struct Node{
    int next[26];
    int len,link;
}st[maxn];
void init(){
    cnt=la=0;
    st[0].link=-1;
}
void add(int c){
    int cur=++cnt;
    st[cur].len=st[la].len+1;
    int p;
    for(p=la;p!=-1&&st[p].next[c]==0;p=st[p].link)st[p].next[c]=cur;
    if(p==-1)st[cur].link=0;
    else{
        int q=st[p].next[c];
        if(st[q].len==st[p].len+1)st[cur].link=q;
        else{
            int clone=++cnt;
            st[clone]=st[q];
            st[clone].len=st[p].len+1;
            for(;p!=-1&&st[p].next[c]==q;p=st[p].link)st[p].next[c]=clone;
            st[q].link=st[cur].link=clone;
        }
    }la=cur;
}
LL Go(int u){
    if(vis[u])return dp[u];
    vis[u]=true;dp[u]=1;
    for(int i=0;i<26;++i){
        if(st[u].next[i])dp[u]+=Go(st[u].next[i]);
    }return dp[u];
}
void Get_C(LL k){
    int now=0;
    while(1){
        for(int i=0;i<26;++i){
            if(st[now].next[i]){
                int v=st[now].next[i];
                if(k>dp[v])k-=dp[v];
                else {c[++clen]=i+'a';now=v;break;}
            }
        }k--;
        if(k==0)return;
    }return;
}
LL Hash_C(int L,int R){return H[L]-H[R+1]*xp[R-L+1];}
LL Hash_S(int L,int R){return h[L]-h[R+1]*xp[R-L+1];}
bool cmp(int a,int b){
    if(c[1]<s[a])return false;
    else if(c[1]>s[a])return true;
    int L=a,R=min(L+clen-1,b);
    while(L<R){
        int mid=L+((R-L+1)>>1);
        if(Hash_C(1,mid-a+1)!=Hash_S(a,mid))R=mid-1;
        else L=mid;
    }L++;
    if(L>b)return true;
    else if(L-a+1>clen)return false;
    if(c[L-a+1]<s[L])return false;
    else if(c[L-a+1]>s[L])return true;
}
bool check(){
    int ans=0,p;
    for(int i=n;i;i=p){
        p=i;
        while(p&&cmp(p,i))p--;
        if(p==i)return false;
        ans++;
    }return ans<=k;
}
 
int main(){
    scanf("%d",&k);
    scanf("%s",s+1);n=strlen(s+1);
    init();
    for(int i=1;i<=n;++i)add(s[i]-'a');
    Go(0);xp[0]=1;h[n+1]=0;
    for(int i=1;i<=n;++i)xp[i]=xp[i-1]*x;
    for(int i=n;i>=1;--i)h[i]=h[i+1]*x+s[i]-'a';
    LL L=1,R=dp[0]-1;
    while(L<R){
        LL mid=(L+R)>>1;
        clen=0;Get_C(mid);
        H[clen+1]=0;
        for(int i=clen;i>=1;--i)H[i]=H[i+1]*x+c[i]-'a';
        if(check())R=mid;
        else L=mid+1;
    }
    clen=0;Get_C(R);
    for(int i=1;i<=clen;++i)printf("%c",c[i]);
    printf("\n");return 0;
     
}

  

posted @ 2016-04-04 20:34  _Vertical  阅读(502)  评论(0编辑  收藏  举报