Gym - 101194F Mr. Panda and Fantastic Beasts [2016-2017 ACM-ICPC CHINA-Final] [后缀数组]

点击打开题目

题意: 求第一个字符串在其他字符串中未出现过的最短, 且字典序最小的子串.

分析: 先将所有子串用特殊字符串连接起来, 用后缀数组算出子串的sa数组和lcp高度数组, 因为sa数组是按后缀从小到大排得, 然后从后往前遍历, 遇到第i个位置, 若sa[i] < s1.length()且与sa[j] > s1.length()位置的最长前缀比当前记录的前缀更小, 然后判断这个最长前缀是不是s1字符串的后缀, 如果不是, 则更新最小值. 例如: 已知s1 = “dddffa”, 和字符串s2 = “dfa”, 在得到s1中的’a’和s2中的’a’是最长前缀之后, 发现’a’已经是s1的后缀, 所以s1中以’a’开头的子串一定在s2中出现, 此时不更新答案.


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<cmath>
#include<string>
typedef long long ll;
const int maxn = 3 * 1e5 + 10;
const ll mod = 1e9 + 7;
using namespace std;
int v = 1;
ll b[maxn], gg[maxn];
int T, n, k, r;

int rankx[maxn], sa[maxn], tmp[maxn], lcp[maxn];
string s, t;
bool cmp(int i, int j) {
    if(rankx[i] != rankx[j]) return rankx[i] < rankx[j];
    int ri = i + k <= n ? rankx[i + k] : -1;
    int rj = j + k <= n ? rankx[j + k] : -1;
    return ri < rj;
}

void sa_(string st) {
    n = st.length();
    for(int i = 0; i <= n; i++) {
        sa[i] = i;
        rankx[i] = i < n ? st[i] : -1;
    }
    for(k = 1; k <= n; k *= 2) {
        sort(sa, sa + n + 1, cmp);
        tmp[sa[0]] = 0;
        for(int i = 1; i <= n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
        }
        for(int i = 0; i <= n; i++) {
            rankx[i] = tmp[i];
        }
    }
}

void lcp_(string st) {
    int n = st.length();
    for(int i = 0; i <= n; i++) rankx[sa[i]] = i;
    int h = 0;
    lcp[0] = 0;
    for(int i = 0; i < n; i++ ) {
        int j = sa[rankx[i] - 1];
        if(h > 0) h--;
        for(; j + h < n && i + h < n; h++) {
            if(st[j + h] != st[i + h]) break;
        }
        lcp[rankx[i] - 1] = h;
    }
}

void solve() {
    int ans = r - 1, gg = -1;
    for(int i = n; i >= 1; i--) {
        while(sa[i] >= r) i--;
        if(i < 1) break;
        int res = lcp[i - 1], rs = lcp[i];
        for(int p = i - 1; p && sa[p] < r; p--) {
            res = min(res, lcp[p]);
        }
        for(int p = i + 1; p <= n && sa[p] < r; p++) {
            rs = min(rs, lcp[p]);
        }
        res = max(res, rs);
        if(res < r - sa[i]) {
            if(res <= ans) {
                ans = res;
                gg = sa[i];
            }
        }
    }
    printf("Case #%d: ", v++);
    if(gg == -1) printf("Impossible\n");
    else {
        for(int i = gg; i <= gg + ans; i++) {
            printf("%c", s[i]);
        }
        printf("\n");
    }
}

int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        s = "";
        cin >> s;
        r = s.length();
        char c = 'z' + 1;
        for(int i = 1; i < n; i++) {
            cin >> t;
            s += c + t;
        }
        int l = s.length();
        sa_(s);
        lcp_(s);
//        for(int i = 1; i <= l; i++) {
//            printf("%d %d\n", sa[i], lcp[i]);
//        }
        solve();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值