zoukankan      html  css  js  c++  java
  • [bzoj4310]跳蚤

    来自FallDream的博客,未经允许,请勿转载,谢谢。


    很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。
    首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。
    现在他想找一个最优的分法让“魔力串”字典序最小。
    |S|<=10^5
     
    考虑后缀数组求出所有子串,然后二分答案是哪一个子串,从后往前判断,需要切就切开即可。
    求出height之后,不同的子串个数是$sum{n-sa[i]-Height[i]+1}$ 这个比较好理解。  找第k大的时候从小到大计算即可。
    然后判断可以借用lcp,用st表来查询即可
    复杂度nlogn
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MN 100000
    #define MD 17
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    long long tot=0;
    int K,n,sa[2][MN+5],rk[2][MN+5],v[MN+5],k,Log[MN+5],F[MD+1][MN+5],p=1,L,R,q=0,H[MN+5];
    char st[MN*2+5];
    
    void CalSa(int*SA,int*RK,int*sa,int*rk)
    {
        for(int i=1;i<=n;++i) v[rk[sa[i]]]=i;
        for(int i=n;i;--i)
            if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
        for(int i=n-k+1;i<=n;++i) SA[v[rk[i]]--]=i;
        for(int i=1;i<=n;++i)
            RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
    }
    
    void GetH(int*sa,int*rk)
    {
        for(int i=1,k=0;i<=n;H[rk[i++]]=k,k?--k:0) if(rk[i]>1)
            for(int j=sa[rk[i]-1];st[i+k]==st[j+k];++k);
    }
    
    int Query(int l,int r)
    {
        int s=Log[r-l+1];
        return min(F[s][l],F[s][r-(1<<s)+1]);
    }
    
    bool Cmp(int l,int r,int L,int R)
    {
        if(l==L){return r>R;}
        int rk1=rk[q][l],rk2=rk[q][L],lcp=Query(min(rk1,rk2)+1,max(rk1,rk2));
        if(lcp>=max(r-l+1,R-L+1)) return false;
        if(lcp>=min(r-l+1,R-L+1)) return (r-l>R-L);
        return st[l+lcp]>st[L+lcp];
    }
    
    int Check()
    {
        int sum=0,Rt=n;
        for(int i=n;i;--i) if(Cmp(i,Rt,L,R)) Rt=i,++sum;
        return sum+1;
    }
    
    void GetKth(long long Rk)
    {
        for(int i=1;i<=n;++i)
        {
            int num=n-sa[q][i]-H[i]+1;
            if(Rk>num) Rk-=num;
            else {R=(L=sa[q][i])+H[i]+Rk-1;return;}
        }
    }
    
    int main()
    {
        K=read();scanf("%s",st+1);n=strlen(st+1);
        for(int i=1;i<=n;++i) ++v[st[i]-'a'];
        for(int i=1;i<=26;++i) v[i]+=v[i-1];
        for(int i=1;i<=n;++i) sa[q][v[st[i]-'a']--]=i;
        for(int i=1;i<=n;++i) rk[q][sa[q][i]]=rk[q][sa[q][i-1]]+(st[sa[q][i]]!=st[sa[q][i-1]]);
        for(k=1;k<n;k<<=1)
        {
            CalSa(sa[p],rk[p],sa[q],rk[q]);
            swap(p,q);
        }
        GetH(sa[q],rk[q]);Log[0]=-1;
        for(int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
        for(int i=1;i<=n;++i) F[0][i]=H[i];
        for(int i=1;i<=MD;++i)
            for(int j=1;j<=n;++j)
            {
                int l=j+(1<<i-1);
                F[i][j]=min(F[i-1][j],l<=n?F[i-1][l]:n);
            }
        for(int i=1;i<=n;++i) tot+=n-sa[q][i]+1-H[i];
        long long l=1,r=tot,mid,res;
        while(l<=r)
        {
            GetKth(mid=l+r>>1);
            if(Check()<=K) res=mid,r=mid-1;
            else l=mid+1;
        }
        GetKth(res);
        for(int i=L;i<=R;++i) printf("%c",st[i]);
        return 0;
    }
  • 相关阅读:
    再谈算法复杂度
    Android 升级ADT到22第三方Jar包导致的ClassNotFoundException和NoClassDefFoundError异常解决
    spring security 3.1 实现权限控制
    Mysql又一次整理笔记--woods备忘
    从头认识Spring-3.8 简单的AOP日志实现(注解版)-扩展添加检查订单功能,以便记录并检測输入的參数
    Knockout JS 演示样例
    gulp初探
    [android] 线性布局和布局的组合
    [android] 相对布局和单位简介
    [android] 短信发送器
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj4310.html
Copyright © 2011-2022 走看看