UVA11107 Life Forms --- 后缀数组

UVA11107 Life Forms

 

题目描述:

求出出现在一半以上的字符串内的最长字符串。

 

数据范围:

\(\sum len(string) <= 10^{5}\)

 

非常坑的题目。

思路非常好想。

构造出后缀数组。

二分出\(len\)后用\(height\)分组

记\(bel(i)\)表示排名为\(i\)后缀属于哪一个串

当同一组内的不同的\(bel(i)\)出现了\(n/2\)时,本组内有一组解。

 

注意:

每行数据间要打一个空行

 

#include <cstdio>
#include <cstring>
#include <iostream>
#define sid 200050
#define ri register int
using namespace std;

template <typename re>
inline void upmax(re &a, re b) { if(a < b) a = b; }

int Tt, n, ml, ned;
char s[sid];
int bel[sid], sc[sid];
int flag[200];
int sa[sid], rk[sid], cnt[sid], p1[sid], p2[sid], ht[sid];

inline void Suffix() {
    int m = 128;
    int *t1 = p1, *t2 = p2;
    for(ri i = 1; i <= n; i ++) sc[i] = (s[i] == 1) ? ++ m : s[i];
    for(ri i = 1; i <= n; i ++) t1[i] = sc[i];
    for(ri i = 1; i <= m; i ++) cnt[i] = 0;
    for(ri i = 1; i <= n; i ++) cnt[t1[i]] ++;
    for(ri i = 1; i <= m; i ++) cnt[i] += cnt[i - 1];
    for(ri i = n; i >= 1; i --) sa[cnt[t1[i]] --] = i;
    for(ri k = 1; k <= n; k <<= 1) {
        int p = 0;
        for(ri i = 0; i <= m; i ++) t2[i] = 0;
        for(ri i = n - k + 1; i <= n; i ++) t2[++ p] = i;
        for(ri i = 1; i <= n; i ++) if(sa[i] > k) t2[++ p] = sa[i] - k;
        for(ri i = 1; i <= m; i ++) cnt[i] = 0;
        for(ri i = 1; i <= n; i ++) cnt[t1[t2[i]]] ++;
        for(ri i = 1; i <= m; i ++) cnt[i] += cnt[i - 1];
        for(ri i = n; i >= 1; i --) sa[cnt[t1[t2[i]]] --] = t2[i];
        swap(t1, t2); t1[sa[1]] = p = 1;
        for(ri i = 2; i <= n; i ++) 
        t1[sa[i]] = (t2[sa[i]] == t2[sa[i - 1]] && t2[sa[i] + k] == t2[sa[i - 1] + k]) ? p : ++ p;
        m = p; if(p >= n) break; 
    }
    for(ri i = 1; i <= n; i ++) rk[sa[i]] = i;
    ri k = 1, j;
    for(ri i = 1; i <= n; i ++) {
        if(k) k --;
        j = sa[rk[i] - 1];
        while(sc[j + k] == sc[i + k]) k ++;
        ht[rk[i]] = k;
    } 
}

inline bool Check(int htk) {
    int cnt = 0, tim = 0;
    memset(flag, 0, sizeof(flag));
    for(ri i = 1; i <= n; i ++) {
        if(ht[i] < htk) cnt = 0, ++ tim;
        if(flag[bel[sa[i]]] != tim && bel[sa[i]]) cnt ++, flag[bel[sa[i]]] = tim;
        if(cnt >= ned) return 1;
    }
    return 0;
}

inline int Binary() {
    int l = 1, r = ml, ans = -1;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(Check(mid)) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    return ans;
}

inline void Get_Ans(int op) {
    if(op == -1) {
        printf("?\n");
        return;
    }
    memset(flag, 0, sizeof(flag));
    int cnt = 0, tim = 0, fag;
    for(ri i = 1; i <= n; i ++) {
        if(ht[i] < op) fag = 0, cnt = 0, ++ tim;
        if(flag[bel[sa[i]]] != tim && bel[sa[i]]) cnt ++, flag[bel[sa[i]]] = tim;
        if(cnt >= ned && !fag) {
            int k = sa[i];
            for(ri j = 1; j <= op; j ++) printf("%c", s[k + j - 1]);
            printf("\n");
            cnt = 0; fag = 1;
        }
    }
}

int main() {
    bool pe = 0;
    while(scanf("%d", &Tt) == 1 && Tt) {
        if(pe) printf("\n"); pe = 1;
        n = ml = 0; ned = Tt / 2 + 1;
        memset(s, 0, sizeof(s));
        memset(bel, 0, sizeof(bel));
        for(ri i = 1; i <= Tt; i ++) {
            scanf("%s", s + 1 + n);
            int nl = strlen(s + 1 + n);
            for(ri j = n + 1; j <= n + nl; j ++) bel[j] = i;
            upmax(ml, nl); n += nl; s[++ n] = 1;
        }
        if(Tt != 1) {
            Suffix();
            int ans = Binary();
            Get_Ans(ans);
        }
        else {
            for(ri j = 1; j <= n - 1; j ++) printf("%c", s[j]);
            printf("\n");
        }
    }
    return 0;
}
打开有惊喜

转载于:https://www.cnblogs.com/reverymoon/p/8971188.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值