bzoj 4310 跳蚤

http://www.elijahqi.win/archives/3231
Description

很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。首先,他会把串
分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k
个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最大。

Input

第一行一个整数 k,K<=15
接下来一个长度不超过 10^5 的字符串 S。

Output

输出一行,表示字典序最大的“魔力串”。

Sample Input

2
ababa
Sample Output

ba
//解释:
分成aba和ba两个串,其中字典序最大的子串为ba
题意:分成最多k段 取出每段里字典序最大的子串
再把这取出的所有子串中选一个字典序最大的 求整个串字典序最大的最小
首先二分 二分我答案是字典序第几小的串 (不重复中第k小
然后找到这个第k小的在哪里 贪心的针对全局构造 使得不存在构造之后出现比我大的子串 如果满足说明二分的正确缩小范围 因为显然字典序越靠后的 越容易满足答案
如何找第k小的 需要在排好序的后缀从前往后扫 即可
如何贪心 如果发现我当前这个长度区间比我第k小要大 那么就想办法把我的首字母切掉,如果首字母比较大说明第k小 不符合答案

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
ll sum;int K,mn[N][17],Log[N],rk[N<<1],rk1[N<<1],ansl,ansr;
int cnt[N],height[N],tmp[N],sa[N],n;char s[N];
inline int lcp(int x,int y){
    if (x==y) return n-x+1;
    x=rk[x];y=rk[y];if (x>y) swap(x,y);x+=1;//printf("%d %d\n",x,y); 
    int t=Log[y-x+1];//printf("%d\n",t);
    return min(mn[x][t],mn[y-(1<<t)+1][t]);
}
inline void init(){
    Log[0]=-1;
    for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
    for (int i=1;i<=n;++i) mn[i][0]=height[i];
    for (int j=1;j<=Log[n];++j)
        for (int i=1;i+(1<<j)-1<=n;++i)
            mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
}
inline void get_k(ll k){
    static ll now;now=0;
    for (int i=1;i<=n;++i){
        now=n-sa[i]-height[i]+1;
        if (now<k) {k-=now;continue;}
        ansl=sa[i];ansr=sa[i]+k+height[i]-1;break;
    }
}
inline bool cmp(int l1,int r1,int l2,int r2){//s[l1,r1]<=s[l2,r2]->1
    static int len1,len2,lp;len1=r1-l1+1;len2=r2-l2+1;lp=lcp(l1,l2);
    if (lp>=len2&&len1>len2) return 0;
    if (lp>=len1&&len2>=len1) return 1;
    if (lp>=len1&&lp>=len2) return len1<=len2;
    return s[l1+lp]<=s[l2+lp];
}
inline bool check(){static int lst,tim;lst=n;tim=1;
    for (int i=n;i;--i){
        if (s[i]>s[ansl]) return 0;
        if (!cmp(i,lst,ansl,ansr)) lst=i,++tim;
        if (tim>K) return 0;
    }return 1;
}
int main(){
    freopen("bzoj4310.in","r",stdin);
    scanf("%d",&K);scanf("%s",s+1);n=strlen(s+1);
    for (int i=1;i<=n;++i) cnt[s[i]]=1;int m=300; 
    for (int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
    for (int i=1;i<=n;++i) rk[i]=cnt[s[i]];int k=0;
    for (int p=1;k!=n;p<<=1,m=k){
        for (int i=1;i<=m;++i) cnt[i]=0;
        for (int i=1;i<=n;++i) ++cnt[rk[i+p]];
        for (int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
        for (int i=n;i;--i) tmp[cnt[rk[i+p]]--]=i;
        for (int i=1;i<=m;++i) cnt[i]=0;
        for (int i=1;i<=n;++i) ++cnt[rk[i]];
        for (int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
        for (int i=n;i;--i) sa[cnt[rk[tmp[i]]]--]=tmp[i];
        memcpy(rk1,rk,sizeof(rk)>>1);k=rk[sa[1]]=1;
        for (int i=2;i<=n;++i){
            if (rk1[sa[i]]!=rk1[sa[i-1]]||rk1[sa[i-1]+p]!=rk1[sa[i]+p]) ++k;
            rk[sa[i]]=k;
        }
    }k=0;
    for (int i=1;i<=n;++i){
        if (rk[i]==1) continue;k=k==0?0:k-1;
        while(s[i+k]==s[sa[rk[i]-1]+k]) ++k;
        height[rk[i]]=k;
    }init();for (int i=1;i<=n;++i) sum+=i,sum-=height[i];
/*  for (int i=1;i<=n;++i){
        for (int j=sa[i];j<=n;++j) putchar(s[j]);puts("");
    }*/
//  for (int i=1;i<=n;++i) printf("%d\n",height[i]);
    ll l=1,r=sum,mid;int L,R;
    while(l<=r){
        get_k(mid=l+r>>1);
        if (check()) L=ansl,R=ansr,r=mid-1;else l=mid+1;
    }for (int i=L;i<=R;++i) putchar(s[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值