题目大意:
要求将一个长串分解成最多k个子串,使得分开的n个串的字典序最大的那一个子串的字典序最小。
思路分析:
要最大的最小,不难想到二分的。
我们二分出原串中的第rk大子串就是目标串。
现在就是怎么判断这个串满足要求,也就是我们如何分其他部分,使之成为字典序最大的一个。
我们可以通过rk轻易的找到这是哪一个串,假设它处在sa[t]中。
那么可以知道 在 sa数组中t以前的子串的字典序都是比目标串小的。
而后面会有比sa大的,我们就要分解这些串。
我们从t 扫描 到n的height ,如果有一个height为0了,那么肯定是不行的,因为这个串的第一个都大。然后考虑不是0 的情况,那么就是说在这个子串的后面再加一个原串的下一个字母,就会比目标串大。所以这里就要切割,使之比目标串小。
然后最后判断你分割了多少个串,是否比k小。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define maxn 100005
using namespace std;
typedef long long ll;
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
void suffix(const char *str,int n,int m)
{
int *x=t1,*y=t2;
for(int i=0; i<m; i++)c[i]=0;
for(int i=0; i<n; i++)c[x[i]=str[i]]++;
for(int i=1; i<m; i++)c[i]+=c[i-1];
for(int i=n-1; i>=0; i--)sa[--c[x[i]]]=i;
for(int k=1; k<=n; k<<=1)
{
int p=0;
for(int i=n-k; i<n; i++)y[p++]=i;
for(int i=0; i<n; i++)if(sa[i]>=k)y[p++]=sa[i]-k;
for(int i=0; i<m; i++)c[i]=0;
for(int i=0; i<n; i++)c[x[y[i]]]++;
for(int i=0; i<m; i++)c[i]+=c[i-1];
for(int i=n-1; i>=0; i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1; i<n; i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
if(p>=n)break;
m=p;
}
}
int rank[maxn],height[maxn];
void getheight(const char *str,int n)
{
int k=0;
for(int i=0; i<n; i++)rank[sa[i]]=i;
for(int i=0; i<n; i++)
{
if(k)k--;
if(!rank[i])continue;
int j=sa[rank[i]-1];
while(str[i+k]==str[j+k])k++;
height[rank[i]]=k;
}
}
char str[maxn];
ll sum[maxn];
int n,k;
int cc[maxn];
bool ok(ll rk)
{
int t=lower_bound(sum+1,sum+n,rk)-sum;
int l=sa[t];
int r=sa[t]+rk-sum[t-1]+height[t]-1;
int len=r-l+1;//找到目标串
for(int i=0;i<n;i++)cc[i]=-1;//如果cc不等于-1,就代表在i切下了一个子串 [i,i+len].
if(sa[t]+len<n-1)
cc[sa[t]]=len;//如果这个串是不用切割的,也就是他的结尾就是原串的末尾
for(int i=t+1;i<n;i++)
{
len=min(len,height[i]);
if(len==0)return false;
if(sa[i]+len<n-1)
cc[sa[i]]=len;
}
int ans=0;
r=n;
for(int i=0;i<n;i++)
{//在切割的时候有些问题,就是前面的时候已经切割到自己这一段区间,就可以跳过。
if(i==r)
{
ans++;
r=n;
if(ans>=k)return false;
}
if(cc[i]!=-1)
{
r=min(r,i+cc[i]);
}
}
return ans<k;//分解成了ans+1段
}
int main()
{
while(scanf("%d",&k)!=EOF && k)
{
scanf("%s",str);
n=strlen(str)+1;
suffix(str,n,128);
getheight(str,n);
sum[0]=0;
for(int i=1;i<n;i++)
sum[i]=sum[i-1]+(n-sa[i]-height[i]-1);
ll l=1,r=n*n,mid,ans=1;
while(l<=r)
{
mid=(l+r)>>1;
if(ok(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
int t=lower_bound(sum+1,sum+n,ans)-sum;
int ll=sa[t];
int rr=sa[t]+ans-sum[t-1]+height[t]-1;
for(int i=ll;i<=rr;i++)
printf("%c",str[i]);
puts("");
}
return 0;
}