zoukankan      html  css  js  c++  java
  • [2016北京集训测试赛1]兔子的字符串-[后缀数组+二分]

    Description

    Solution

    由于这道题很难计算出一个答案,我们考虑二分。

    既然要二分,我们需要能在很短时间内求出字符串的大小关系,可以考虑后缀数组(它可以直接把后缀排序,还可以算相邻串的公共前缀)。

    将所有的后缀从小到大排完序后,我们二分某个后缀,使它为答案,判断划分的段数。

    假如我们目前查找到的串的左端点固定,易知其右端点向右挪的过程中,答案绝对不会变小。(满足贪心性质)

    贪心,假如目前二分到后缀i,定义dis[j],满足s[j,dis[j]+j-1]内的字典序最大的子串<=后缀i,且dis[j]要尽量大。

    则排名在i之前(即字典序比后缀i小)的后缀,dis[j]直接为len(s的长度)就好,因为它可以延伸到字符串末尾的,你设为正无穷也可以,反正它绝对不会影响最后划分的段数。但是字典序比i大的后缀j,它的dis[j]只能为它和后缀i的最大公共前缀了。

    确定了答案在某个后缀i里后,在后缀i里继续二分即可。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int n,sa[100010],rk[100010],height[100010],cnt[100010];
    char s[100010];int k;
    bool check(int *x,int a,int b,int j)
    {
        if (a+j>n&&b+j>n) return x[a]==x[b];
        if (a+j>n||b+j>n) return 0;
        return x[a]==x[b]&&x[a+j]==x[b+j];
    }
    void build_sa()
    {
        int *x=rk,*y=height,m='z';
        for (int i=1;i<=n;i++) cnt[x[i]=s[i]]++;
        for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for (int i=n;i;i--) sa[cnt[x[i]]--]=i;
        for (int j=1,pos=0;pos<n;m=pos,j<<=1)
        {
            pos=0;
            for (int i=n-j+1;i<=n;i++) y[++pos]=i;
            for (int i=1;i<=n;i++) if (sa[i]>j) y[++pos]=sa[i]-j;
            for (int i=1;i<=m;i++) cnt[i]=0;
            for (int i=1;i<=n;i++) cnt[x[y[i]]]++;
            for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for (int i=n;i;i--) sa[cnt[x[y[i]]]--]=y[i];
            swap(x,y);
            pos=1;x[sa[1]]=1;
            for (int i=2;i<=n;i++) x[sa[i]]=check(y,sa[i-1],sa[i],j)?pos:++pos;  
        }
    }
    void get_height()
    {
        int k=0;
        for (int i=1;i<=n;i++) rk[sa[i]]=i;
        for (int i=1;i<=n;height[rk[i++]]=k)
            for (k?--k:0;s[i+k]==s[sa[rk[i]-1]+k];k++);
    }
    int dis[100010];
    int solve(int l,int r)
    {
        for (int i=1;i<=n;i++) dis[i]=n;
        dis[l]=r-l+1;
        for (int i=rk[l]+1;i<=n;i++) 
        {
            dis[sa[i]]=min(dis[sa[i-1]],height[i]);
            if (!dis[sa[i]]) return n+1;
        }
        int mn=n+1,num=0;
        for (int i=1;i<=n;i++)
        {
            if (mn<i) num++,mn=n+1;
            mn=min(mn,i+dis[i]-1);
        }
        num++;
        return num;
    }
    int main()
    {
        scanf("%d%s",&k,s+1);
        n=strlen(s+1);
        build_sa();
        get_height();
        int l=1,r=n,mid;
        while (l<r)
        {
            mid=(l+r)/2;
            if (solve(sa[mid],n)>k) l=mid+1;else r=mid;
        }
        l=sa[l];
        int l1=1,r1=n-l+1;
        while (l1<r1)
        {
            mid=(l1+r1)/2;
            if (solve(l,l+mid-1)>k) l1=mid+1;
            else r1=mid;
        }
        for (int i=l;i<l+l1;i++) printf("%c",s[i]);
        
    }

     

  • 相关阅读:
    Java中“==”与equals的区别以及equals方法的重写
    Java中的Switch....case语句:
    Java中的基本数据类型
    HTTP与HTTPS的区别
    深入理解HTTP协议、HTTP协议原理分析
    IntelliJ IDEA下的使用git
    Spring RestTemplate详解
    Java 适配器(Adapter)模式
    LINUX的ssh免密码配置
    CDH6.2安装配置第一篇:CDH配置本地http服务
  • 原文地址:https://www.cnblogs.com/coco-night/p/9565051.html
Copyright © 2011-2022 走看看