Luogu

分析

先考虑 $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;
}
最后修改:2021 年 03 月 24 日 03 : 03 PM