[XSY 1516] 兔子的字符串 后缀数组
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[XSY 1516] 兔子的字符串 后缀数组相关的知识,希望对你有一定的参考价值。
题意
给定一个字符串 $S$ .
按照某种方式, 将字符串 $S$ 化成不超过 $K$ 段 $S_1, S_2, ..., S_K$ .
每段 $S_i$ 有字典序最大的子串 $C_i$ .
最小化 $C_i$ 的最大值.
$N \le 200000$ .
分析
通过后缀数组, 先二分后缀, 再二分长度, 实现二分所有的字符串.
判定则可以贪心取, 利用后缀数组的信息, 记录 v[i] 表示位置 i 不能与位置 v[i] 在同一段中.
实现
#include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> using namespace std; #define F(i, a, b) for (register int i = (a); i <= (b); i++) #define P(i, a, b) for (register int i = (a); i >= (b); i--) const int N = 100005; int K, n; char s[N]; int sa[N], rk[N], h[N], tsa[N], trk[N], sum[N]; int v[N]; void Prework(void) { int m = 500; F(i, 1, n) sum[s[i]]++; F(i, 1, m) sum[i] += sum[i-1]; P(i, n, 1) sa[sum[s[i]]--] = i; rk[sa[1]] = m = 1; F(i, 2, n) rk[sa[i]] = (s[sa[i]] != s[sa[i-1]] ? ++m : m); for (int j = 1; m != n; j <<= 1) { int p = 0; F(i, n-j+1, n) tsa[++p] = i; F(i, 1, n) if (sa[i] > j) tsa[++p] = sa[i]-j; memset(sum, 0, sizeof sum); F(i, 1, n) sum[rk[i]]++; F(i, 1, m) sum[i] += sum[i-1]; P(i, n, 1) sa[sum[rk[tsa[i]]]--] = tsa[i]; memcpy(trk, rk, sizeof rk); rk[sa[1]] = m = 1; F(i, 2, n) rk[sa[i]] = ((trk[sa[i]] != trk[sa[i-1]] || trk[sa[i]+j] != trk[sa[i-1]+j]) ? ++m : m); } m = 0; F(i, 1, n) { if (m > 0) m--; while (s[sa[rk[i]-1]+m] == s[i+m]) m++; h[rk[i]] = m; } } inline bool Check(int M) { int H = n, cnt = 1; F(i, M+1, n) { H = min(H, h[i]); v[sa[i]] = sa[i]+H; if (!h[i]) { cnt = K+1; break; } } for (int i = 1, go = n+1; i <= n && cnt <= K; i++) { if (go == i) cnt++, go = n+1; if (v[i] > 0) go = min(go, v[i]); } F(i, M+1, n) v[sa[i]] = 0; return cnt <= K; } inline bool C(int x, int M) { int H = M, cnt = 1; v[sa[x]] = sa[x]+M; F(i, x+1, n) { H = min(H, h[i]); v[sa[i]] = sa[i]+H; if (!h[i]) { cnt = K+1; break; } } for (int i = 1, go = n+1; i <= n && cnt <= K; i++) { if (go == i) cnt++, go = n+1; if (v[i] > 0) go = min(go, v[i]); } F(i, x, n) v[sa[i]] = 0; return cnt <= K; } int main(void) { #ifndef ONLINE_JUDGE freopen("rabbit.in", "r", stdin); #endif scanf("%d%s", &K, s+1); n = strlen(s+1); Prework(); int L = 1, R = n; while (L < R) { int M = (L+R)>>1; Check(M) ? R = M : L = M+1; } int x = L; L = h[x]+1, R = n-sa[x]+1; while (L < R) { int M = (L+R)>>1; C(x, M) ? R = M : L = M+1; } F(i, sa[x], sa[x]+L-1) putchar(s[i]); puts(""); return 0; }
以上是关于[XSY 1516] 兔子的字符串 后缀数组的主要内容,如果未能解决你的问题,请参考以下文章