hdu 5030 Rabbit's String 后缀数组+二分

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5030

Rabbit's String

Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 190    Accepted Submission(s): 65


Problem Description
Long long ago, there lived a lot of rabbits in the forest. One day, the king of the rabbit kingdom got a mysterious string and he wanted to study this string.

At first, he would divide this string into no more than k substrings. Then for each substring S, he looked at all substrings of S, and selected the one which has the largest dictionary order. Among those substrings selected in the second round, the king then choose one which has the largest dictionary order, and name it as a "magic string".

Now he wanted to figure out how to divide the string so that the dictionary order of that "magic string" is as small as possible.
 

Input
There are at most 36 test cases.

For each test case, the first line contains a integer k indicating the maximum number of substrings the king could divide, and the second line is the original mysterious string which consisted of only lower letters.

The length of the mysterious string is between 1 and 10 5 and k is between 1 and the length of the mysterious string, inclusive.
  
The input ends by k = 0.
 

Output
For each test case, output the magic string.
 

Sample Input

3 bbaa 2 ababa 0
 

Sample Output

b ba
Hint
For the first test case, the king may divide the string into "b", "b" and "aa". For the second test case, the king may divide the string into "aba" and "ba".
 

Source
2014 ACM/ICPC Asia Regional Guangzhou Online


题目大意:给一个字符串s,你最多可以将s分成k个字符串,对于这些子串,s1,s2,s2...sn(n<=k),要使这些所有子串的子串最大值最小,输出这个答案。

大致思路:先orz下雷哥,本来没多少思路,看了雷哥博客才懂。使最大值最小可以想到二分答案去判断,我们二分这个答案串是字符串的第x小子串,为字符串A,根据后缀数组可以很容易确定A的位置,就假设就出现在sa[t],那么对于从排名t到n的后缀我们都可能要进行分割。在排名[t,n]的后缀中截取子串s[Li,Ri],且该子串和A有大于0的公共前缀,对于t当然就是A本身。如果公共前缀为0,就说明这个后缀的第一个子串大于A的第一个字符,这种情况肯定不成立。这些子串具有的性质的是:再加上它的后面的一个字符都比答案串大,所以都应该切割一下。这里要注意Ri应该小于n,等于n就是最后一个字符了,最多和答案串相等。

这样就转换成了这样一个问题,有一些区间[Li,Ri],只要区间里的一个点被覆盖这个区间就被覆盖了(这个点就是切割点),求最少的点覆盖这些区间。如[4,10],[5,20],[6,22],[30,40]只需切割10和40就行,又如[1,9],[6,7],[7,8][8,10],[10,12],只需切割7,10即可。

贪心即可。可以知道区间范围是[0,n-1],我们从左到右依次找最小的右边界,这个点肯定需要切割。。。

代码:

/**
 * @author neko01
 */
//#pragma comment(linker, "/STACK:102400000,102400000")
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define clr(a) memset(a,0,sizeof a)
#define clr1(a) memset(a,-1,sizeof a)
#define dbg(a) printf("%d\n",a)
typedef pair pp;
const double eps=1e-9;
const double pi=acos(-1.0);
const int N=100005;
int sa[N]; //排第几的是哪个后缀
//sa[1~n]为有效值,sa[0]必定为n是无效值
int rank[N]; //rank后缀i排第几
//rank[0~n-1]为有效值,rank[n]必定为0无效值
int height[N]; //sa[i]和sa[i-1]的最长公共前缀
//height[2~n]为有效值
int t1[N],t2[N],c[N];
void build_sa(int s[],int n,int m)
{
    int *x=t1,*y=t2;
    //第一轮基数排序
    for(int i=0;i=0;i--) sa[--c[x[i]]]=i;
    for(int j=1;j<=n;j<<=1)
    {
        int p=0;
        //直接利用sa数组排序第二关键字
        for(int i=n-j;i=j) y[p++]=sa[i]-j;
        //基数排序第一关键字
        for(int i=0;i=0;i--) sa[--c[x[y[i]]]]=y[i];
        //根据sa和x数组计算新的x数组
        swap(x,y);
        p=1,x[sa[0]]=0;
        for(int i=1;i=n) break;
        m=p;
    }
}
void getheight(int s[],int n)
{
    int k=0;
    for(int i=0;i<=n;i++)
        rank[sa[i]]=i;
    for(int i=0;i=m) return false;
        }
        if(c[i]!=-1)
            r=min(r,i+c[i]);
    }
    return ans>1;
            if(gao(mid))
            {
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        int t=lower_bound(f+1,f+1+n,ans)-f;
        int l1=sa[t],l2=n-(f[t]-ans+1);
        for(int i=l1;i<=l2;i++)
            printf("%c",s[i]);
        puts("");
    }
    return 0;
}


你可能感兴趣的:(hdu,后缀数组)