• 【BZOJ-4310】跳蚤 后缀数组 + ST表 + 二分


    4310: 跳蚤

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 180  Solved: 83
    [Submit][Status][Discuss]

    Description

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

    Input

    第一行一个整数 k。
    接下来一个长度不超过 105 的字符串

    Output

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

    Sample Input

    13
    bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba

    Sample Output

    cbc

    HINT

    S的长度<=100000

    Source

    Solution

    首先求出后缀数组和height数组,这样能得到本质不同的子串数目

    这里利用:本质不同的子串$=sum(Len-SA[i]-height[i])$利用SA[],height[]的定义很好想

    然后要求最大值最小,显然二分,二分一个mid,求出第mid大的子串

    然后贪心的检验,从后往前扫,当字典序超过二分的值时,划分一下,看划分个数与K的关系即可

    中间涉及比较,用LCP实现即可,显然ST表非常方便

    Code

    #include<cstdio> 
    #include<algorithm> 
    #include<cstring> 
    using namespace std; 
    #define maxn 1000100 
    char S[maxn]; int SA[maxn],len,K; 
    int wa[maxn],wb[maxn],ws[maxn],wv[maxn]; 
    long long tot; 
    int L,R; 
    inline int cmp(int *r,int a,int b,int l) 
    { 
        return r[a]==r[b]&&r[a+l]==r[b+l]; 
    } 
    inline void DA(char *r,int *sa,int n,int m) 
    { 
        int p,*x=wa,*y=wb,*t; 
        for (int i=0; i<m; i++) ws[i]=0; 
        for (int i=0; i<n; i++) ws[x[i]=r[i]]++; 
        for (int i=1; i<m; i++) ws[i]+=ws[i-1]; 
        for (int i=n-1; i>=0; i--) sa[--ws[x[i]]]=i; 
        p=1; for (int j=1; p<n; j*=2,m=p) 
            { 
                p=0; for (int i=n-j; i<n; i++) y[p++]=i; 
                for (int i=0; i<n; i++) if (sa[i]>=j) y[p++]=sa[i]-j; 
                for (int i=0; i<n; i++) wv[i]=x[y[i]]; 
                for (int i=0; i<m; i++) ws[i]=0; 
                for (int i=0; i<n; i++) ws[wv[i]]++; 
                for (int i=1; i<m; i++) ws[i]+=ws[i-1]; 
                for (int i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i]; 
                t=x,x=y,y=t;p=1;x[sa[0]]=0; 
                for (int i=1; i<n; i++) 
                    x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 
            } 
    } 
    int rank[maxn],height[maxn]; 
    inline void calheight(char *r,int *sa,int n) 
    { 
        int k=0; 
        for (int i=1; i<=n; i++) rank[sa[i]]=i; 
        for (int i=0; i<n; height[rank[i++]]=k) 
            {k?k--:0;for (int j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);} 
    } 
    int log2[maxn]; int dp[maxn][21]; 
    void ST(int n) 
    { 
        log2[0]=-1; 
        for (int i=1; i<=n; i++)  
            if (i&(i-1)) log2[i]=log2[i-1]; 
                else log2[i]=log2[i-1]+1; 
        for (int i=1; i<=n; i++) dp[i][0]=height[i]; 
        for (int j=1; (1<<j)<=len; j++) 
            for (int i=1; i+(1<<j)-1<=n; i++) 
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); 
    } 
    int RMQ(int l,int r) 
    { 
        int tmp=log2[r-l+1]; 
        return min(dp[l][tmp],dp[r-(1<<tmp)+1][tmp]); 
    } 
    int LCP(int l,int r) 
    { 
        if (l==r) return len-l; 
        l=rank[l]; r=rank[r];  
        if (l>r) swap(l,r); l++; 
        return RMQ(l,r); 
    } 
    void Get(long long k) 
    { 
        for (int i=1; i<=len; i++) 
            if ((long long)(len-SA[i]-height[i])<k) k-=(long long)(len-SA[i]-height[i]); 
                else {L=SA[i],R=SA[i]+height[i]+k-1; break;} 
    } 
    bool Compare(int l1,int r1,int l2,int r2) 
    { 
        int len1=r1-l1+1,len2=r2-l2+1,lcp=LCP(l1,l2); 
        if (len1<=len2 && lcp>=len1) return 1; 
        if (len1>len2 && lcp>=len2) return 0; 
        if (lcp>=len1 && lcp>=len2) return len1>len2? 0:1; 
        return S[l1+lcp]>S[l2+lcp]? 0:1;  
    } 
    int Check() 
    { 
        int cnt=1,last=len-1; 
        for (int i=len-1; i>=0; i--) 
            { 
                if (S[i]>S[L]) return 0; 
                if (!Compare(i,last,L,R)) ++cnt,last=i; 
                if (cnt>K) return 0; 
            } 
        return 1; 
    } 
    int main() 
    { 
        scanf("%d",&K); scanf("%s",S); 
        len=strlen(S); S[len]=0; 
        DA(S,SA,len+1,200); calheight(S,SA,len); 
        ST(len); 
        for (int i=1; i<=len; i++) tot+=len-SA[i]-height[i]; 
    //    printf("%d
    ",tot); 
        long long l=1,r=tot; 
        while (l<=r) 
            { 
                long long mid=(l+r)>>1; 
                Get(mid); 
    //          printf("L=%d R=%d
    ",L,R); 
                if (Check()) r=mid-1; else l=mid+1; 
    //          printf("%I64d %I64d
    ",l,r); 
            } 
        Get(r+1); 
        for (int i=L; i<=R; i++) putchar(S[i]); 
        return 0; 
    }
  • 相关阅读:
    [Vim].vimrc
    [笔试面试][code_by_hand]压缩字符串
    [笔试面试][code_by_hand]输出第二个字符串在第一个字符串中的连接次序
    [笔试面试]串s1="ABCDACDAE" s2="DAE" 找出s1中,包含s2的最小子串,要求该子串含有s2中的所有字符,串内字符无顺序关系
    [笔试面试][code_by_hand]从二元树中找出和为某一值的所有路径
    DEV 皮肤的使用
    Dev GridControl使用小结
    [转载]DEV控件:gridControl常用属性设置
    C#网页 自动登录 提交POST信息 方法
    比较字符串NSString
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5713899.html
Copyright © 2020-2023  润新知