zoukankan      html  css  js  c++  java
  • HDU 5030 Rabbit'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),且该子串和A具有大于0的公共前缀(若这些之中存在与A的公共前缀为0的后缀则A不成立),那么这样的子串有啥性质呢?他们的性质就是他们加上他们在原串中的下一个字母后组成的串都比答案串A大(很显然吧。因为我们找的都是排名在A之后的后缀截取的)。那么,理论上来讲,这样的串一出现,我们就要截取一下,因为他们不能和后面一个字母挨在一起。

    但是,看下面的情况,比如所有这样的串为:[4,10],[5,15],[5,20],[6,11],[6,25],[30,40],首先我们要在10的位置截开,这样[4,10]就不会跟后面一个字母挨在一起了,然后随着这一截开,[5,15],[5,20],[6,11],[6,25]这些串都被分成两段了,所以他们不用再被截开了,之后再从40位置截开。最后截了两次分成了三段。

    const int INF=1000000005;
    const int N=111111;
    
    int r[N],sa[N],wa[N],wb[N],wd[N],rank[N],h[N];
    
    
    int cmp(int *r,int a,int b,int len)
    {
        return r[a]==r[b]&&r[a+len]==r[b+len];
    }
    
    
    void da(int *r,int *sa,int n,int m)
    {
        int i,j,p,*x=wa,*y=wb,*t;
        FOR0(i,m) wd[i]=0;
        FOR0(i,n) wd[x[i]=r[i]]++;
        FOR1(i,m-1) wd[i]+=wd[i-1];
    	for(i=n-1;i>=0;i--) sa[--wd[x[i]]]=i;
        for(j=1,p=1;p<n;j<<=1,m=p)
        {
            p=0;
    		for(i=n-j;i<=n-1;i++)y[p++]=i;
            FOR0(i,n) if(sa[i]>=j) y[p++]=sa[i]-j;
            FOR0(i,m) wd[i]=0;
            FOR0(i,n) wd[x[i]]++;
            FOR1(i,m-1) wd[i]+=wd[i-1];
    		for(i=n-1;i>=0;i--) sa[--wd[x[y[i]]]]=y[i];
            t=x;x=y;y=t;p=1;x[sa[0]]=0;
            FOR1(i,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    
    
    void calHeight(int *r,int *sa,int n)
    {
        int i,j,k=0;
        FOR1(i,n) rank[sa[i]]=i;
        FOR0(i,n)
        {
            if(k) k--;
            j=sa[rank[i]-1];
            while(i+k<n&&j+k<n&&r[i+k]==r[j+k]) k++;
            h[rank[i]]=k;
        }
    }
    
    char s[N];
    int K;
    int n;
    
    i64 f[N];
    
    vector<pii > V,p;
    
    int cal()
    {
    	if(SZ(V)==0) return 0;
    	sort(all(V));
    	int minR=INF;
    	p.clear();
    	int i;
    	for(i=SZ(V)-1;i>=0;i--)
    	{
    		if(minR<=V[i].second) continue;
    		minR=V[i].second;
    		p.pb(V[i]);
    	}
    	int ans=0,last=-1;
    	sort(all(p));
    	for(i=0;i<SZ(p);i++)
    	{
    		if(last>=p[i].first) continue;
    		ans++;
    		last=p[i].second;
    	}
    	return ans;
    }
    
    
    int ansL,ansR;
    
    int ok(i64 M)
    {
    	int t=lower_bound(f+1,f+n+1,M)-f;
    	int L=sa[t];
    	int len=h[t]+M-f[t-1];
    
    
    	ansL=L;
    	ansR=L+len-1;
    
    	V.clear();
    	if(L+len<n) V.pb(MP(L,L+len-1));
    	int i;
    	for(i=t+1;i<=n;i++)
    	{
    		len=min(len,h[i]);
    		if(!len) return 0;
    		if(sa[i]+len<n) V.pb(MP(sa[i],sa[i]+len-1));
    	}
    	return cal()<K;
    }
    
    void print()
    {
    	int i;
    	for(i=ansL;i<=ansR;i++) putchar(s[i]); puts("");
    }
    int main()
    {
    
    	while(scanf("%d",&K)!=-1)
    	{
    		if(!K) break;
    		scanf("%s",s);
    		n=strlen(s);
    		int i;
    		for(i=0;i<n;i++) r[i]=s[i]-'a'+1;
    		r[n]=0;
    		da(r,sa,n+1,30);
    		calHeight(r,sa,n);
    
    		for(i=1;i<=n;i++) f[i]=f[i-1]+n-sa[i]-h[i];
    
    		i64 low=1,high=f[n],ans=f[n];
    		while(low<=high)
    		{
    			i64 M=(low+high)>>1;
    
    			if(ok(M)) ans=min(ans,M),high=M-1;
    			else low=M+1;
    		}
    		ok(ans);
    		print();
    	}
    }
     
    
  • 相关阅读:
    [LeetCode in Python] 98 (M) validate binary search tree 验证二叉搜索树
    [LeetCode in Python] 79 (M) word search 单词搜索
    mybatis3源码阅读之SqlSessionFactoryBuilder
    java简单的双色球摇号程序
    低延时直播应用 流媒体
    glPixelStorei 详解 包括像素传输
    depth/stencil buffer的作用 ----------理解模板缓存 opengl
    实用网站汇编
    利用CodeBlocks结合freeglut快速搭建OpenGL开发环境
    opengl 4.5 中文api 链接
  • 原文地址:https://www.cnblogs.com/jianglangcaijin/p/3985315.html
Copyright © 2011-2022 走看看