【题目大意】
有一个只有小写字母的字符串,你要把其分成不超过k个不重叠的连续子串。对于每个子串,求其字典序最大的子串(为了与前者区分,我们称为子串2)。所有子串2,有一个字典序最大的串,我们称为目标串。问,怎么分,使得目标串字典序最小,输出这个目标串。
【思路】
很有意思的一题。首先,目标串一定是原串的一个子串。那么可以考虑去二分目标串(即二分所有子串)。
这里有个问题,怎么确定原串的第k大串是什么?这个问题是HDU 5008的简化版,我在以前博客中有写。
现在剩下的问题,就是对于确定串S(这个串是原串的子串),使得它成为目标串,至少把原串分为几部分?
这里可以贪心求解,举个例子:使“ba”成为“bbaa”的目标串,至少分几部分。
记re[i]表示,以原串的第i个字符做起点,求一个最大re[i],使得原串的子串[i,re[i]]字典序小于等于目标串“ba”。
bbaa的re[]为
下标:0 1 2 3
re[ ]:0 2 3 3
我们从左到右,贪心地加字符,能加就加,不能加就隔断,最后有几段,就是分几部分。具体怎么贪心,看下面代码。
ans = 0;
temp = -1;
for(i = 0; i < n; i ++)
{
if(re[i] < i)
return INF;
if(i > temp)
{
ans++;
temp = re[i];
}
else
temp = min(temp, re[i]);
}
return ans;
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF = 100011122;
const double INFF = 1e100;
const double eps = 1e-8;
const int mod = 1000000007;
const int NN = 100010;
const int MM = 5000010;
/* ****************** */
char ss[NN];
int a[NN], re[NN];
LL sum[NN];
int wa[NN],wb[NN],wv[NN],wss[NN],sa[NN];
int rank[NN],height[NN];
int rmq[NN][20],long2[NN];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
//r[n-1]==0;
void da(int *r,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++)wss[i]=0;
for(i=0;i<n;i++)wss[x[i]=r[i]]++;
for(i=1;i<m;i++)wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--)sa[--wss[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p)
{
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;
for(i=0;i<n;i++)wv[i]=x[y[i]];
for(i=0;i<m;i++)wss[i]=0;
for(i=0;i<n;i++)wss[wv[i]]++;
for(i=1;i<m;i++)wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--)sa[--wss[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++;
}
return;
}
void calheight(int *r,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++)rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}
void initRMQ(int n)
{
int i,j,en;
long2[1]=0;
for(i=2;i<=n;i++)
{
long2[i]=long2[i-1]+(i==(i&(-i)));
}
for(i=1;i<=n;i++)rmq[i][0]=height[i];
for(j=1;j<=long2[n];j++)
{
en=n+1-(1<<j);
for(i=1;i<=en;i++)
rmq[i][j]=min(rmq[i][j-1],rmq[ i+(1<<(j-1)) ][j-1]);
}
}
int lcp(int i,int j)
{
i=rank[i],j=rank[j];
if(i>j)swap(i,j);
i++;
int k=long2[j-i+1];
int ans=min(rmq[i][k],rmq[ j+1-(1<<k) ][k]);
return ans;
}
int solve(int st,int l,int len,int n)
{
int i, temp, ans;
for(i = 0; i < n; i ++)
{
if(i==l)
{
re[i] = l + len - 1;
continue;
}
temp = lcp(i, l);
temp = min(temp, len);
if(rank[i] < st)
{
if(temp == len)
re[i] = i + temp - 1;
else
re[i] = n-1;
}
else
{
re[i] = i + temp - 1;
}
}
ans = 0;
temp = -1;
for(i = 0; i < n; i ++)
{
if(re[i] < i)
return INF;
if(i > temp)
{
ans++;
temp = re[i];
}
else
temp = min(temp, re[i]);
}
return ans;
}
int ok(LL k,int n)
{
if(k==0)return INF;
int st = lower_bound(sum+1, sum+1+n, k) - sum;
int l = sa[st];
int len = height[st] + k - sum[st-1];
return solve(st, l, len, n);
}
int main()
{
int n, k, i;
LL l, r, mid;
while(scanf("%d",&k)!=EOF)
{
if(k==0)break;
scanf("%s", ss);
n = strlen(ss);
for(i = 0; i < n; i ++)
{
a[i] = ss[i]- 'a' +1;
}
a[n] = 0;
da(a,n+1,40);
calheight(a,n);
initRMQ(n);
sum[0] = 0;
for(i = 1; i <= n; i ++)
{
sum[i] = n - sa[i] - height[i];
sum[i] += sum[i-1];
}
l = 0;
r = sum[n];
while (l + 1 < r)
{
mid = (l + r)>>1;
if(ok(mid,n) <= k)
r = mid;
else
l = mid;
}
int st = lower_bound(sum+1, sum+1+n, r) - sum;
int ll = sa[st];
int rr = ll + height[st] + r - sum[st-1] - 1;
for(i = ll; i <= rr; i ++)
{
printf("%c",ss[i]);
}
puts("");
}
return 0;
}