• 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();
    	}
    }
     
    
  • 相关阅读:
    ER模型
    一道人人的笔试题
    关系代数运算
    推荐两个不错的CAD二次开发(.Net)手册
    CAD 致命错误
    CAD二次开发(.NET)之PaletteSet和Palette
    养生
    我看面向对象
    .NET中参数化查询数据库
    自定义按照index和key访问的List
  • 原文地址:https://www.cnblogs.com/jianglangcaijin/p/3985315.html
Copyright © 2020-2023  润新知