【广义sam】Gym101194F Mr. Panda and Fantastic Beasts

S o u r c c e : Sourcce: Sourcce:2016-2017 ACM-ICPC CHINA-Final
P r o b l e m : Problem: Problem:给n个串,找一个最短且字典序最小的子串只在第一个串中出现过。
I d e a : Idea: Idea:
如果不需要字典序最小,可以二分答案+hash或者对后n-1个串建广义sam,跑失配,但是这样比较最小字典序就很麻烦(虽然暴力比能过)。
考虑对n个串建广义sam,并且将剩下n-1个串所有子串都进行标记,那么只要在DAG上跑一个字典序最小的最短路就好了,找到第一个未被标记的点。
C o d e : Code: Code:

#include
using namespace std;

#define I inline
#define fi first
#define se second
#define pb push_back
#define CLR(A, X) memset(A, X, sizeof(A))
typedef long long LL;
typedef unsigned long long ULL;
typedef pair PII;

const int N = 3e5+10, SIGMA = 26;

char str[N];

struct SAM {
    int last, sz;
    int ch[N<<1][SIGMA], len[N<<1], f[N<<1], c[N<<1], q[N<<1];
    bool vis[N<<1], d[N<<1];
    PII pre[N<<1];

    void init() {
        for(int i = 1; i <= sz; i++) {
            CLR(ch[i], 0);
            d[i] = vis[i] = 0;
        }
        last = sz = 1;
        f[1] = len[1] = 0;
    }

    void insert(int c) {
        int p = last;
        if(ch[p][c]) {
            int q = ch[p][c];
            if(len[q] == len[p]+1) last = q;
            else {
                int nq = last = ++sz;
                len[nq] = len[p]+1;
                memcpy(ch[nq], ch[q], sizeof(ch[q]));
                f[nq] = f[q]; f[q] = nq;
                while(p && ch[p][c]==q) ch[p][c] = nq, p = f[p];
            }
        }
        else {
            int np = last = ++sz;
            len[np] = len[p] + 1;
            while(!ch[p][c] && p) ch[p][c] = np, p = f[p];
            if(!p) f[np] = 1;
            else {
                int q = ch[p][c];
                if(len[q] == len[p]+1) f[np] = q;
                else {
                    int nq = ++sz;
                    len[nq] = len[p]+1;
                    memcpy(ch[nq], ch[q], sizeof(ch[q]));
                    f[nq] = f[q]; f[np] = f[q] = nq;
                    while(p && ch[p][c]==q) ch[p][c] = nq, p = f[p];
                }
            }
        }
    }

    void build() {
        for(int i = 1; i <= sz; i++) c[i] = 0;
        for(int i = 1; i <= sz; i++) c[len[i]]++;
        for(int i = 1; i <= sz; i++) c[i] += c[i-1];
        for(int i = sz; i >= 1; i--) q[c[len[i]]--] = i;
        for(int i = sz; i >= 1; i--) vis[f[q[i]]] |= vis[q[i]];
    }

    queue Q;
    int bfs() {
        while(!Q.empty()) Q.pop();
        int u = 1; Q.push(u); d[u] = 1;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop();
            for(int i = 0; i < 26; i++) {
                int v = ch[u][i];
                if(v && !d[v]) {
                    d[v] = 1;
                    pre[v] = {u, i};
                    Q.push(v);
                    if(!vis[v]) return v;
                }
            }
        }
        return -1;
    }

    void prt(int u) {
        if(u == 1) return;
        prt(pre[u].fi);
        printf("%c", pre[u].se+'a');
    }
}A;

string s[N];

I void work() {
    A.init();
    int n; scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%s", str);
        s[i] = (string)str;
        int len = s[i].size();
        A.last = 1;
        for(int j = 0; j < len; j++) {
            A.insert(str[j]-'a');
            if(i) A.vis[A.last] = 1;
        }
    }
    A.build();

    static int cas = 0;
    printf("Case #%d: ", ++cas);

    int u = A.bfs();
    if(u == -1) puts("Impossible");
    else A.prt(u), puts("");
}

int main() {
    int X; scanf("%d", &X);
    while(X--) work();
    return 0;
}

你可能感兴趣的:(数据结构-后缀自动机)