[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] 兔子的字符串 后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

2016vijos 1-1 兔子的字符串(后缀数组 + 二分 + 哈希)

[xsy1260]短信加密

XSY3347串后缀

●后缀数组○十三个例题

XSY2569火神的鱼(线段树+树状数组)

xsy2111 CODECHEFChef and Churus 分块+树状数组