hihocoder 1036 Trie图 (AC自动机)

输入

每个输入文件有且仅有一组测试数据。

每个测试数据的第一行为一个整数N,表示河蟹词典的大小。

接下来的N行,每一行为一个由小写英文字母组成的河蟹词语。

接下来的一行,为一篇长度不超过M,由小写英文字母组成的文章。

对于60%的数据,所有河蟹词语的长度总和小于10, M<=10

对于80%的数据,所有河蟹词语的长度总和小于10^3, M<=10^3

对于100%的数据,所有河蟹词语的长度总和小于10^6, M<=10^6, N<=1000

输出

对于每组测试数据,输出一行"YES"或者"NO",表示文章中是否含有河蟹词语。

样例输入
6
aaabc
aaac
abcc
ac
bcd
cd
aaaaaaaaaaabaaadaaac
样例输出
YES

题目链接:https://hihocoder.com/problemset/problem/1036

题目分析:裸的AC自动机,其思想是在Trie树的基础上增加失败转移,利用当前失配的后缀是某条路径的前缀性质,这很像KMP,可以省去很多重复的比较

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int const MAX = 1e6 + 5;
char s[MAX], t[MAX];

struct Trie {
    Trie *nxt[26];
    Trie *fail;
    int cnt;

    Trie() {
        memset(nxt, 0, sizeof(nxt));
        fail = NULL;
        cnt = 0;
    }
};

void Insert(Trie *root, char *s) {
    int len = strlen(s);
    Trie *p = root;
    for (int i = 0; i < len; i++) {
        int idx = s[i] - 'a';
        if (p -> nxt[idx] == NULL) {
            p -> nxt[idx] = new Trie();
        }
        p = p -> nxt[idx];
    }
    p -> cnt ++;
}

void Build_AC(Trie *root) {
    queue <Trie*> q;
    q.push(root);
    while (!q.empty()) {
        Trie *cur = q.front();
        q.pop();
        for (int i = 0; i < 26; i++) {
            if (cur -> nxt[i] == NULL) {
                if (cur == root) {
                    cur -> nxt[i] = root;
                } else {
                    cur -> nxt[i] = cur -> fail -> nxt[i];
                }
            } else {
                if (cur == root) {
                    cur -> nxt[i] -> fail = root;
                } else {
                    cur -> nxt[i] -> fail = cur -> fail -> nxt[i];
                }
                q.push(cur -> nxt[i]);
            }
        }
    }
}

bool Query(Trie *root, char *t) {
    int len = strlen(t);
    Trie *p = root;
    for (int i = 0; i < len; i++) {
        int idx = t[i] - 'a';
        while (p -> nxt[idx] == NULL && p != root) {
            p = p -> fail;
        }
        p = p -> nxt[idx];
        if (p == NULL) {
            p = root;
        }
        Trie *tmp = p;
        while (tmp != root && tmp -> cnt != -1) {
            if (tmp -> cnt > 0) {
                return true;
            }
            tmp -> cnt = -1;
            tmp = tmp -> fail;
        }   
    }
    return false;
}

int main() {
    int n;
    scanf("%d", &n);
    Trie *root = new Trie();
    while (n--) {
        scanf("%s", s);
        Insert(root, s);
    }
    scanf("%s", t);
    Build_AC(root);
    printf("%s\n", Query(root, t) ? "YES" : "NO");
}

猜你喜欢

转载自blog.csdn.net/Tc_To_Top/article/details/79638738