[POI2000]公共串

给出不超过5个字符串,求最长公共子串

总长度不超过1w

 

把几个串接到一起中间用不同的字符隔开

求出height之后,二分答案为k,在height数组中找到每一段连续的且均不小于k的数,用前缀和判断里面是否包含了来自每一个字符串的子串

$O(nlogn)$

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10000 + 10;
int n, m;
char s[maxn]; 
int sa[maxn], rank[maxn], height[maxn];
int tax[maxn], tp[maxn];
inline void tsort(){
    for(int i = 1; i <= m; i++) tax[i] = 0;
    for(int i = 1; i <= n; i++) tax[rank[i]]++;
    for(int i = 2; i <= m; i++) tax[i] += tax[i - 1];
    for(int i = n; i; i--) sa[tax[rank[tp[i]]]--] = tp[i];
}
inline bool cmp(int *arr, int l, int r, int k){
    return arr[l] == arr[r] && arr[l + k] == arr[r + k];
}
void suffix_sort(){
    m = 128;
    for(int i = 1; i <= n; i++) tp[i] = i;
    for(int i = 1; i <= n; i++) rank[i] = s[i];
    tsort();
    for(int k = 1, p = 0; p < n; k <<= 1, m = p){
        p = k;
        for(int i = 1; i <= k; i++) tp[i] = n - k + i;
        for(int i = 1; i <= n; i++) if(sa[i] > k) tp[++p] = sa[i] - k;
        tsort();
        swap(rank, tp);
        p = rank[sa[1]] = 1;
        for(int i = 2; i <= n; i++)
            rank[sa[i]] = cmp(tp, sa[i - 1], sa[i], k) ? p : ++p;
    }
    for(int i = 1, j, k = 0; i <= n; height[rank[i++]] = k)
        for(k ? k-- : 0, j = sa[rank[i] - 1]; s[j + k] == s[i + k]; k++);
}
int L[10], R[10], sum[10][maxn] = {0};
char temp[] = {'!', '@', '#', '$', 0};
int q[maxn], qcnt;
int main(){
    int N;
    scanf("%d", &N);
    n = 0;
    for(int i = 0; i < N; i++){
        L[i] = n + 1;
        scanf("%s", s + 1 + n);
        n += strlen(s + 1 + n);
        R[i] = n;
        s[++n] = temp[i];
    }
    s[n--] = 0;
    suffix_sort();
    for(int i = 0; i < N; i++){
        for(int j = L[i]; j <= R[i]; j++)
            sum[i][rank[j]]++;
        for(int j = 1; j <= n; j++)
            sum[i][j] += sum[i][j - 1];
    }
    int l = 1, r = maxn, mid, ans = 0;
    for(int i = 0; i < N; i++) r = min(r, R[i] - L[i] + 1);
    bool flag;
    while(l <= r){
        mid = l + r >> 1;
        qcnt = 0;
        for(int i = 1; i <= n; i++){
            if(height[i] < mid) q[++qcnt] = i;
        }
        for(int i = 2; i <= qcnt; i++){
            flag = true;
            for(int j = 0; j < N; j++) flag &= sum[j][q[i] - 1] - sum[j][q[i - 1] - 1] > 0;
            if(flag) break;
        }
        if(flag){
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
}

 

posted @ 2019-09-26 20:45  Elder_Giang  阅读(161)  评论(0编辑  收藏  举报