【题目链接】
【思路要点】
- 倍长字符串,求出其后缀数组,并保留后缀数组中值在\(N\)以内的元素,这些即是环形字符串后缀排序的结果。
- 答案的位数是已知的,为\(\lfloor\frac{N-1}{K}\rfloor+1\)。
- 从小到大枚举每一个答案,问题转化成能否将字符串从当前位置开始分成至多\(K\)段使得每一段小于当前答案。
- 这个问题显然可以\(O(N)\)贪心,只要使得每次取的字符串在不超过当前答案的情况下尽可能长就行了。
- 用分块加速这个过程,单次复杂度降至\(O(\sqrt{N})\)。
- 时间复杂度\(O(NLogN+N\sqrt{N})\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 400005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } namespace SuffixArray { const int MAXN = 400005; const int MAXLOG = 20; const int MAXC = 256; int sa[MAXN], rank[MAXN], height[MAXN]; int Min[MAXN][MAXLOG], bit[MAXN], N; void init(char *a, int n) { N = n; static int x[MAXN], y[MAXN], cnt[MAXN], rk[MAXN]; memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[(int) a[i]]++; for (int i = 1; i <= MAXC; i++) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) sa[cnt[(int) a[i]]--] = i; rank[sa[1]] = 1; for (int i = 2; i <= n; i++) rank[sa[i]] = rank[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]]); for (int k = 1; rank[sa[n]] != n; k <<= 1) { for (int i = 1; i <= n; i++) { x[i] = rank[i]; y[i] = (i + k <= n) ? rank[i + k] : 0; } memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[y[i]]++; for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) rk[cnt[y[i]]--] = i; memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[x[i]]++; for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) sa[cnt[x[rk[i]]]--] = rk[i]; rank[sa[1]] = 1; for (int i = 2; i <= n; i++) rank[sa[i]] = rank[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]); } int now = 0; for (int i = 1; i <= n; i++) { if (now) now--; while (a[i + now] == a[sa[rank[i] + 1] + now]) now++; height[rank[i]] = now; } for (int i = 1; i <= n; i++) Min[i][0] = height[i]; for (int p = 1; p < MAXLOG; p++) { int tmp = 1 << (p - 1); for (int i = 1, j = tmp + 1; j <= n; i++, j++) Min[i][p] = min(Min[i][p - 1], Min[i + tmp][p - 1]); } for (int i = 1; i <= n; i++) { bit[i] = bit[i - 1]; if (i >= 1 << (bit[i] + 1)) bit[i]++; } } int lcp(int x, int y) { if (x == y) return N - x + 1; x = rank[x], y = rank[y]; if (x > y) swap(x, y); int tmp = bit[y - x]; return min(Min[x][tmp], Min[y - (1 << tmp)][tmp]); } } int n, m, k; int t, p[MAXN]; int l[MAXN], r[MAXN]; int nxt[MAXN], cnt[MAXN]; int size, tot, belong[MAXN]; char s[MAXN]; bool boost[MAXN]; int main() { read(n), read(k); scanf("%s", s + 1); for (int i = 1; i <= n; i++) s[i + n] = s[i]; SuffixArray::init(s, 2 * n); m = n / k + (n % k != 0); if (m == 1) { int Max = 0; for (int i = 1; i <= n; i++) chkmax(Max, s[i] - '0'); writeln(Max); return 0; } for (int i = 1; i <= 2 * n; i++) if (SuffixArray::sa[i] <= n) p[++t] = SuffixArray::sa[i]; size = pow(n, 0.5); for (int i = 1; i <= n; i++) { if (i % size == 1 % size) l[++tot] = i; r[tot] = i, belong[i] = tot; } for (int i = 1; i <= tot; i++) for (int j = r[i]; j >= l[i]; j--) { int tmp = j + m - 1; if (tmp <= r[i]) cnt[j] = cnt[tmp] + 1, nxt[j] = nxt[tmp]; else cnt[j] = 0, nxt[j] = j; } for (int i = 1; i <= n; i++) { int st = p[i]; boost[st] = true; for (int j = st; j >= l[belong[st]]; j--) { int tmp = j + m - 1 + boost[j]; if (tmp <= r[i]) cnt[j] = cnt[tmp] + 1, nxt[j] = nxt[tmp]; else cnt[j] = 0, nxt[j] = j; } int now = st, steps = 0, length = 0; while (steps < k) { if (steps + cnt[now] + 1 <= k) { steps += cnt[now]; length += nxt[now] - now; now = nxt[now]; steps += 1; length += m - !boost[now]; now += m - !boost[now]; if (now > n) now -= n; } else { steps += 1; length += m - !boost[now]; now += m - !boost[now]; if (now > n) now -= n; } } if (length >= n) { for (int i = 1; i <= m; i++) printf("%c", s[st + i - 1]); printf("\n"); return 0; } } return 0; }