分析
先考虑 $k|n$ 时的做法。显然所有串的长度应相等。枚举第一个串的起始位置,则需要比较 $k$ 个串中最大的,事先倍长然后后缀排序即可利用 $rk$ 比大小。
考虑 $k\nmid n$ 时怎么做。显然最长的串至多比最短的串长 $1$。
考虑二分答案的排名 $mid$,check()
时仍然枚举第一个串的起始位置 $i$,然后记录一个指针 $p$ 一开始等于 $i$,每次判断 $rk_p$ 是否 $\leq mid$,如果是说明往后 $\left\lfloor\frac{n}{k}\right\rfloor+1$ 位可以作为一个串,否则只能将往后 $\left\lfloor\frac{n}{k}\right\rfloor$ 位作为一个串。最后判断是否划分完即可。
代码
// ===================================
// author: M_sea
// website: http://m-sea-blog.com/
// ===================================
#include <bits/stdc++.h>
#define re register
using namespace std;
inline int read() {
int X=0,w=1; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
return X*w;
}
const int N=400000+10;
int n,k; char s[N];
int sa[N],rnk[N],x[N],y[N],z[N];
inline void Suffix_Sort(int n) {
int m=122;
for (re int i=1;i<=n;++i) ++z[x[i]=s[i]];
for (re int i=2;i<=m;++i) z[i]+=z[i-1];
for (re int i=n;i;--i) sa[z[x[i]]--]=i;
for (re int k=1;k<=n;k<<=1) {
int p=0;
for (re int i=n-k+1;i<=n;++i) y[++p]=i;
for (re int i=1;i<=n;++i) if (sa[i]>k) y[++p]=sa[i]-k;
for (re int i=1;i<=m;++i) z[i]=0;
for (re int i=1;i<=n;++i) ++z[x[i]];
for (re int i=2;i<=m;++i) z[i]+=z[i-1];
for (re int i=n;i;--i) sa[z[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y),x[sa[1]]=p=1;
for (re int i=2;i<=n;++i)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p:++p;
if (p==n) break; m=p;
}
for (re int i=1;i<=n;++i) rnk[sa[i]]=i;
}
inline int check(int mid) {
int l=n/k+(n%k!=0);
for (re int i=1;i<=l;++i) {
for (re int j=1,p=i;j<=k;++j) {
rnk[p]<=mid?p+=l:p+=l-1;
if (p>=i+n) return 1;
}
}
return 0;
}
int main() {
n=read(),k=read(); scanf("%s",s+1);
for (re int i=1;i<=n;++i) s[i+n]=s[i];
Suffix_Sort(n<<1);
int L=1,R=n<<1;
while (L<R) {
int mid=(L+R)>>1;
if (check(mid)) R=mid;
else L=mid+1;
}
for (re int i=1;i<=n;++i) {
if (rnk[i]!=L) continue;
for (re int j=0;j<n/k+(n%k!=0);++j) putchar(s[i+j]);
puts(""); break;
}
return 0;
}