首先我们知道我们要求的是使得最大值最小,显然是要二分的
我们先对原串建出后缀自动机
之后二分答案是第k小的字符串
对于答案可行性的判定:
我们注意到对于每一个区间,其字典序最大的子串一定是区间的某个后缀
那么我们不妨从后往前扫,这样每次只会增加一个后缀
我们只需要判断这个后缀是否比当前答案小就可以了
如果比当前答案大,就划分出一组
可以证明,这样分组是在满足条件的情况下分组最少的
至于两个串比较大小,可以用哈希做到O(logn)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef unsigned long long LL;
const int maxn=400010;
const int x=13331;
int k,n,clen;
char s[maxn];
char c[maxn];
bool vis[maxn];
LL dp[maxn];
LL xp[maxn],h[maxn],H[maxn];
int cnt=0,la=0;
struct Node{
int next[26];
int len,link;
}st[maxn];
void init(){
cnt=la=0;
st[0].link=-1;
}
void add(int c){
int cur=++cnt;
st[cur].len=st[la].len+1;
int p;
for(p=la;p!=-1&&st[p].next[c]==0;p=st[p].link)st[p].next[c]=cur;
if(p==-1)st[cur].link=0;
else{
int q=st[p].next[c];
if(st[q].len==st[p].len+1)st[cur].link=q;
else{
int clone=++cnt;
st[clone]=st[q];
st[clone].len=st[p].len+1;
for(;p!=-1&&st[p].next[c]==q;p=st[p].link)st[p].next[c]=clone;
st[q].link=st[cur].link=clone;
}
}la=cur;
}
LL Go(int u){
if(vis[u])return dp[u];
vis[u]=true;dp[u]=1;
for(int i=0;i<26;++i){
if(st[u].next[i])dp[u]+=Go(st[u].next[i]);
}return dp[u];
}
void Get_C(LL k){
int now=0;
while(1){
for(int i=0;i<26;++i){
if(st[now].next[i]){
int v=st[now].next[i];
if(k>dp[v])k-=dp[v];
else {c[++clen]=i+'a';now=v;break;}
}
}k--;
if(k==0)return;
}return;
}
LL Hash_C(int L,int R){return H[L]-H[R+1]*xp[R-L+1];}
LL Hash_S(int L,int R){return h[L]-h[R+1]*xp[R-L+1];}
bool cmp(int a,int b){
if(c[1]<s[a])return false;
else if(c[1]>s[a])return true;
int L=a,R=min(L+clen-1,b);
while(L<R){
int mid=L+((R-L+1)>>1);
if(Hash_C(1,mid-a+1)!=Hash_S(a,mid))R=mid-1;
else L=mid;
}L++;
if(L>b)return true;
else if(L-a+1>clen)return false;
if(c[L-a+1]<s[L])return false;
else if(c[L-a+1]>s[L])return true;
}
bool check(){
int ans=0,p;
for(int i=n;i;i=p){
p=i;
while(p&&cmp(p,i))p--;
if(p==i)return false;
ans++;
}return ans<=k;
}
int main(){
scanf("%d",&k);
scanf("%s",s+1);n=strlen(s+1);
init();
for(int i=1;i<=n;++i)add(s[i]-'a');
Go(0);xp[0]=1;h[n+1]=0;
for(int i=1;i<=n;++i)xp[i]=xp[i-1]*x;
for(int i=n;i>=1;--i)h[i]=h[i+1]*x+s[i]-'a';
LL L=1,R=dp[0]-1;
while(L<R){
LL mid=(L+R)>>1;
clen=0;Get_C(mid);
H[clen+1]=0;
for(int i=clen;i>=1;--i)H[i]=H[i+1]*x+c[i]-'a';
if(check())R=mid;
else L=mid+1;
}
clen=0;Get_C(R);
for(int i=1;i<=clen;++i)printf("%c",c[i]);
printf("\n");return 0;
}