http://www.elijahqi.win/archives/3231
Description
很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。首先,他会把串
分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k
个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最大。
Input
第一行一个整数 k,K<=15
接下来一个长度不超过 10^5 的字符串 S。
Output
输出一行,表示字典序最大的“魔力串”。
Sample Input
2
ababa
Sample Output
ba
//解释:
分成aba和ba两个串,其中字典序最大的子串为ba
题意:分成最多k段 取出每段里字典序最大的子串
再把这取出的所有子串中选一个字典序最大的 求整个串字典序最大的最小
首先二分 二分我答案是字典序第几小的串 (不重复中第k小
然后找到这个第k小的在哪里 贪心的针对全局构造 使得不存在构造之后出现比我大的子串 如果满足说明二分的正确缩小范围 因为显然字典序越靠后的 越容易满足答案
如何找第k小的 需要在排好序的后缀从前往后扫 即可
如何贪心 如果发现我当前这个长度区间比我第k小要大 那么就想办法把我的首字母切掉,如果首字母比较大说明第k小 不符合答案
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
ll sum;int K,mn[N][17],Log[N],rk[N<<1],rk1[N<<1],ansl,ansr;
int cnt[N],height[N],tmp[N],sa[N],n;char s[N];
inline int lcp(int x,int y){
if (x==y) return n-x+1;
x=rk[x];y=rk[y];if (x>y) swap(x,y);x+=1;//printf("%d %d\n",x,y);
int t=Log[y-x+1];//printf("%d\n",t);
return min(mn[x][t],mn[y-(1<<t)+1][t]);
}
inline void init(){
Log[0]=-1;
for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
for (int i=1;i<=n;++i) mn[i][0]=height[i];
for (int j=1;j<=Log[n];++j)
for (int i=1;i+(1<<j)-1<=n;++i)
mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
}
inline void get_k(ll k){
static ll now;now=0;
for (int i=1;i<=n;++i){
now=n-sa[i]-height[i]+1;
if (now<k) {k-=now;continue;}
ansl=sa[i];ansr=sa[i]+k+height[i]-1;break;
}
}
inline bool cmp(int l1,int r1,int l2,int r2){//s[l1,r1]<=s[l2,r2]->1
static int len1,len2,lp;len1=r1-l1+1;len2=r2-l2+1;lp=lcp(l1,l2);
if (lp>=len2&&len1>len2) return 0;
if (lp>=len1&&len2>=len1) return 1;
if (lp>=len1&&lp>=len2) return len1<=len2;
return s[l1+lp]<=s[l2+lp];
}
inline bool check(){static int lst,tim;lst=n;tim=1;
for (int i=n;i;--i){
if (s[i]>s[ansl]) return 0;
if (!cmp(i,lst,ansl,ansr)) lst=i,++tim;
if (tim>K) return 0;
}return 1;
}
int main(){
freopen("bzoj4310.in","r",stdin);
scanf("%d",&K);scanf("%s",s+1);n=strlen(s+1);
for (int i=1;i<=n;++i) cnt[s[i]]=1;int m=300;
for (int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
for (int i=1;i<=n;++i) rk[i]=cnt[s[i]];int k=0;
for (int p=1;k!=n;p<<=1,m=k){
for (int i=1;i<=m;++i) cnt[i]=0;
for (int i=1;i<=n;++i) ++cnt[rk[i+p]];
for (int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
for (int i=n;i;--i) tmp[cnt[rk[i+p]]--]=i;
for (int i=1;i<=m;++i) cnt[i]=0;
for (int i=1;i<=n;++i) ++cnt[rk[i]];
for (int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
for (int i=n;i;--i) sa[cnt[rk[tmp[i]]]--]=tmp[i];
memcpy(rk1,rk,sizeof(rk)>>1);k=rk[sa[1]]=1;
for (int i=2;i<=n;++i){
if (rk1[sa[i]]!=rk1[sa[i-1]]||rk1[sa[i-1]+p]!=rk1[sa[i]+p]) ++k;
rk[sa[i]]=k;
}
}k=0;
for (int i=1;i<=n;++i){
if (rk[i]==1) continue;k=k==0?0:k-1;
while(s[i+k]==s[sa[rk[i]-1]+k]) ++k;
height[rk[i]]=k;
}init();for (int i=1;i<=n;++i) sum+=i,sum-=height[i];
/* for (int i=1;i<=n;++i){
for (int j=sa[i];j<=n;++j) putchar(s[j]);puts("");
}*/
// for (int i=1;i<=n;++i) printf("%d\n",height[i]);
ll l=1,r=sum,mid;int L,R;
while(l<=r){
get_k(mid=l+r>>1);
if (check()) L=ansl,R=ansr,r=mid-1;else l=mid+1;
}for (int i=L;i<=R;++i) putchar(s[i]);
return 0;
}