1. 程式人生 > >[XSY 1516] 兔子的字符串 後綴數組

[XSY 1516] 兔子的字符串 後綴數組

lin log har 所有 pen memset esp stdin lib

題意

  給定一個字符串 $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] 兔子的字符串 後綴數組