HNU 13365 Circle of digits Input hou

Circle of digits Input
Time Limit: 10000ms, Special Time Limit:25000ms, Memory Limit:65536KB
Total submit users: 9, Accepted users: 5
Problem 13365 : No special judgement
Problem description

You are given a circular string of length N that consists of digits '1'..'9'. You want to split it into K continuous non-empty parts. Each of those parts represents a decimal notation of some integer number. Your goal is to find a partition that minimizes the maximum integer from the partition at hand.
For example, if the string is 7654321 and K=3 then the optimal partition is: {176, 54, 32} which has 176 as the maximum number. Note that the string is cyclic, that is the first character goes right after the last one (as in the 176 part of the above example).


Input

The first line of the input contains two integers N and K (3 ≤ N ≤ 100000, 2 ≤ K ≤ N). The second line contains a string of length N which consists only of characters ‘1’..’9’.


Output

Output the value of the maximal number in the optimal partition.


Sample Input
4 2
4321
7 3
7654321
5 5
12321
Sample Output
32
176
3


给你一个1~9的字符串,分成k分,要求最大的数字最小


首先,为了使最大的最小,所以每个数字的长度应该尽可能的相同,也就是最多相差一位,那么可以求出上限长度为len。

然后对后缀进行排序,得到sa、rank、height三个数组。这里要注意的是,我们仅关心每个后缀的前len位。因此应当对rank数组进行一些改编,在前height[i]>=len时,rank相等。

求出了rank后,就可以二分答案了,至于开始位置可以暴力。可以证明暴力其实位置的时间复杂度为O(n)。总的时间复杂度为O(nlogn)。


#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = int(2e5)+10;
int cmp(int *r,int a,int b,int l){
    return (r[a]==r[b]) && (r[a+l]==r[b+l]);
}
// 用于比较第一关键字与第二关键字,
// 比较特殊的地方是,预处理的时候,r[n]=0(小于前面出现过的字符)

int wa[N],wb[N],WS[N],wv[N];
int rank[N],height[N];
void get_sa(int *r,int *sa,int n,int m){ //此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0;i<m;i++) WS[i]=0;
    for(i=0;i<n;i++) WS[x[i]=r[i]]++;
    for(i=1;i<m;i++) WS[i]+=WS[i-1];
    for(i=n-1;i>=0;i--) sa[--WS[x[i]]]=i; //预处理长度为1
    for(j=1,p=1;p<n;j*=2,m=p) //通过已经求出的长度J的SA,来求2*J的SA
    {
        for(p=0,i=n-j;i<n;i++) y[p++]=i; // 特殊处理没有第二关键字的
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //利用长度J的,按第二关键字排序
        for(i=0;i<n;i++) wv[i]=x[y[i]];
        for(i=0;i<m;i++) WS[i]=0;
        for(i=0;i<n;i++) WS[wv[i]]++;
        for(i=1;i<m;i++) WS[i]+=WS[i-1];
        for(i=n-1;i>=0;i--) sa[--WS[wv[i]]]=y[i];  //基数排序部分
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  //更新名次数组x[],注意判定相同的
    }
}
int str[N];
int sa[N];
int n,k,len;
void get_height(int *r,int *sa,int n){ // 此处N为实际长度
    int i,j,k=0;        // height[]的合法范围为 1-N, 其中0是结尾加入的字符
    for(i=1;i<=n;i++) rank[sa[i]]=i;  // 根据SA求RANK
    for(i=0;i<n; height[rank[i++]] = k ) // 定义:h[i] = height[ rank[i] ]
    for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++); //根据 h[i] >= h[i-1]-1 来优化计算height过程

   for(int i = 1;i<=n;i++)if(height[i]>=len)
        rank[sa[i]] = rank[sa[i-1]];else rank[sa[i]] = rank[sa[i-1]]+1;
}


int main() {
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        getchar();
        len = n/k + (n%k==0?0:1);
       // cout<<len<<endl;
        for(int i =0;i<n;i++)
        {
            char ch;
            scanf("%c",&ch);
            str[i+n]= str[i] = ch-'0';
        }
        str[n*2]=0;
        get_sa(str,sa,n*2+1,10);
        get_height(str,sa,n*2);
        int l =1,r=n*2;
        int ans=-1;
        while(l<=r)
        {
            int mid = (l+r)/2;
            bool flag = false;
            for(int i = 0;i<len;i++)
            {
                int t = i;
                for(int j = 0;j<k;j++)
                    if(rank[t]<=rank[sa[mid]])
                        t+=len;
                    else t+=len - 1;
                if(t-i>=n) flag = true;
            }
            if(flag) {ans = sa[mid];r=mid-1;}else l = mid+1;
        }
        for(int i = ans;i<ans+len;i++)
            printf("%d",str[i]);
        printf("\n");
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值