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. |
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");
}
}