Hdu 5030 Rabbit's String (后缀数组)

题目大意:

要求将一个长串分解成最多k个子串,使得分开的n个串的字典序最大的那一个子串的字典序最小。

思路分析:

要最大的最小,不难想到二分的。

我们二分出原串中的第rk大子串就是目标串。

现在就是怎么判断这个串满足要求,也就是我们如何分其他部分,使之成为字典序最大的一个。

我们可以通过rk轻易的找到这是哪一个串,假设它处在sa[t]中。

那么可以知道 在 sa数组中t以前的子串的字典序都是比目标串小的。

而后面会有比sa大的,我们就要分解这些串。

我们从t 扫描 到n的height  ,如果有一个height为0了,那么肯定是不行的,因为这个串的第一个都大。然后考虑不是0 的情况,那么就是说在这个子串的后面再加一个原串的下一个字母,就会比目标串大。所以这里就要切割,使之比目标串小。

然后最后判断你分割了多少个串,是否比k小。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define maxn 100005
using namespace std;
typedef long long ll;
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
void suffix(const char *str,int n,int m)
{
    int *x=t1,*y=t2;
    for(int i=0; i<m; i++)c[i]=0;
    for(int i=0; i<n; i++)c[x[i]=str[i]]++;
    for(int i=1; i<m; i++)c[i]+=c[i-1];
    for(int i=n-1; i>=0; i--)sa[--c[x[i]]]=i;
    for(int k=1; k<=n; k<<=1)
    {
        int p=0;
        for(int i=n-k; i<n; i++)y[p++]=i;
        for(int i=0; i<n; i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        for(int i=0; i<m; i++)c[i]=0;
        for(int i=0; i<n; i++)c[x[y[i]]]++;
        for(int i=0; i<m; i++)c[i]+=c[i-1];
        for(int i=n-1; i>=0; i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1; i<n; i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}
int rank[maxn],height[maxn];
void getheight(const char *str,int n)
{
    int k=0;
    for(int i=0; i<n; i++)rank[sa[i]]=i;
    for(int i=0; i<n; i++)
    {
        if(k)k--;
        if(!rank[i])continue;
        int j=sa[rank[i]-1];
        while(str[i+k]==str[j+k])k++;
        height[rank[i]]=k;
    }
}
char str[maxn];
ll sum[maxn];
int n,k;
int cc[maxn];

bool ok(ll rk)
{
    int t=lower_bound(sum+1,sum+n,rk)-sum;
    int l=sa[t];
    int r=sa[t]+rk-sum[t-1]+height[t]-1;
    int len=r-l+1;//找到目标串
    for(int i=0;i<n;i++)cc[i]=-1;//如果cc不等于-1,就代表在i切下了一个子串 [i,i+len].

    if(sa[t]+len<n-1)
    cc[sa[t]]=len;//如果这个串是不用切割的,也就是他的结尾就是原串的末尾

    for(int i=t+1;i<n;i++)
    {
        len=min(len,height[i]);
        if(len==0)return false;
        if(sa[i]+len<n-1)
        cc[sa[i]]=len;
    }

    int ans=0;

    r=n;

    for(int i=0;i<n;i++)
    {//在切割的时候有些问题,就是前面的时候已经切割到自己这一段区间,就可以跳过。
        if(i==r)
        {
            ans++;
            r=n;
            if(ans>=k)return false;
        }
        if(cc[i]!=-1)
        {
            r=min(r,i+cc[i]);
        }
    }
    return ans<k;//分解成了ans+1段
}
int main()
{
    while(scanf("%d",&k)!=EOF && k)
    {
        scanf("%s",str);
        n=strlen(str)+1;
        suffix(str,n,128);
        getheight(str,n);

        sum[0]=0;
        for(int i=1;i<n;i++)
            sum[i]=sum[i-1]+(n-sa[i]-height[i]-1);

        ll l=1,r=n*n,mid,ans=1;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(ok(mid))
            {
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }

        int t=lower_bound(sum+1,sum+n,ans)-sum;
        int ll=sa[t];
        int rr=sa[t]+ans-sum[t-1]+height[t]-1;

        for(int i=ll;i<=rr;i++)
            printf("%c",str[i]);
        puts("");
    }
    return 0;
}

Hdu 5030 Rabbit's String (后缀数组)

时间: 2024-07-29 13:36:13

Hdu 5030 Rabbit's String (后缀数组)的相关文章

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

hdu 5030 Rabbit&#39;s String(后缀数组)

题目链接:hdu 5030 Rabbit's String 题目大意:给定k和一个字符串,要求将字符串拆分成k个子串.然后将每个子串中字典序最大的子串选出来,组成一个包含k个字符串的集合,要求这个集合中字典序最大的字符串字典序最小. 解题思路:网赛的时候试图搞了一下这道题,不过水平还是有限啊,后缀数组也是初学,只会切一些水题.赛后看了一下别人的题解,把这题补上了. 首先对整个字符串做后缀数组,除了处理出sa,rank,height数组,还要处理处f数组,f[i]表示说以0~sa[i]开头共有多少

HDU 5030 Rabbit&#39;s String

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5030 题意:给出一个长度为n的串S,将S分成最多K个子串S1,S2,……Sk(k<=K).选出每个子串Si(1<=i<=k)的最大子串SSi.最后使得k个SSi的最大值最小. 思路:首先用后缀数组求出所有子串.二分答案串,判定是否存在一种分法满足要求.对于答案串A,设A起始位置所组成的后缀排名为t,在排名为[t+1,n]的后缀中截取子串S[Li,Ri],使得Ri<n(下标1到n),且该

hdu 3518 Boring counting(后缀数组)

Boring counting                                                                       Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description 035 now faced a tough problem,his english teacher gives him

POJ 3729 Facer’s string (后缀数组)

题目大意: 串1中有多少个后缀和 串2中的某个后缀 的lcp 为 k 思路分析: 先找出 长度至少为k的对数有多少. 再找出 至少为k+1的有多少 然后相减. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <map> #include <string> #define maxn 110005 using na

hdu 4691 Front compression (后缀数组)

题目大意: 介绍了一种压缩文本的方式,问压缩前后的文本长度. 思路分析: 后缀数组跑模板然后考虑两次l r之间的lcp. 然后减掉重复的长度. 注意ans2的累加. #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #define maxn 200005 using namespace std; typede

hdu 6194 string string string(后缀数组)

题目链接:hdu 6194 string string string 题意: 给你一个字符串,给你一个k,问你有多少个子串恰好在原串中出现k次. 题解: 后缀数组求出sa后,用height数组的信息去找答案. 每次用k长度的区间去卡height数组,求出该区间的lcp. 该区间的贡献就是ans=lcp-max(height[i],height[i+k]). 如果ans<=0,就不贡献. 比如 2 aaa 后缀数组为: 1 a 2 aa 3 aaa height为 0,1,2 现在扫到[1,2],

hdu 6194 沈阳网络赛--string string string(后缀数组)

题目链接 Problem Description Uncle Mao is a wonderful ACMER. One day he met an easy problem, but Uncle Mao was so lazy that he left the problem to you. I hope you can give him a solution.Given a string s, we define a substring that happens exactly k time

HDU 3518 Boring counting(后缀数组啊 求字符串中不重叠的重复出现至少两次的子串的个数)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3518 Problem Description 035 now faced a tough problem,his english teacher gives him a string,which consists with n lower case letter,he must figure out how many substrings appear at least twice,moreover