HDU-2222-Keywords Search(AC自动机)

这题是AC自动机的模板题,AC自动机是结合了字典树,和KMP两种算法产生的,去年为了学习AC自动机去看了前面说的两种算法,但是可能因为KMP当时理解的不够透彻所以这题当时也只是半背代码的做出来了,没多久就忘了。经过那么久的学习感觉KMP掌握的差不多了,今天回顾了一下AC自动机,并记录一下模板

  • AC自动机
    Accepted 2222 327MS 56848K 1991 B G++
    #include "cstdio"
    #include "cstring"
    #include "cstdlib"
    #include "queue"
    using namespace std;
    const int MAXS = 1000005;
    const int MAXP = 55;
    const int SIZE = 26;
    struct Tree {
        int tail;
        Tree* next[SIZE];
        Tree* fail;
    }* root;
    char p[MAXP], s[MAXS];
    Tree* init() {
        Tree* point = (Tree*)malloc(sizeof(Tree));
        point->tail = 0;
        memset(point->next, NULL, sizeof(point->next));
        point->fail = NULL;
        return point;
    }
    void insert(char* p) {
        Tree* point = root;
        for (int i = 0; p[i]; i++) {
            int index = p[i] - 'a';
            if (point->next[index] == NULL) {
                point->next[index] = init();
            }
            point = point->next[index];
        }
        point->tail++;
    }
    void build() {
        queue<Tree*> q;
        Tree* point;
        Tree* p1;
        Tree* p2;
        q.push(root);
        while (!q.empty()) {
            point = q.front();
            q.pop();
            for (int i = 0; i < SIZE; i++) {
                if (point->next[i] != NULL) {
                    p1 = point->fail;
                    p2 = point->next[i];
                    while (p1 != NULL) {
                        if (p1->next[i] != NULL) {
                            p2->fail = p1->next[i];
                            break;
                        }
                        p1 = p1->fail;
                    }
                    if (p1 == NULL) {
                        p2->fail = root;
                    }
                    q.push(p2);
                }
            }
        }
    }
    int query(char* s) {
        int cnt = 0;
        Tree* p1;
        Tree* p2;
        p1 = root; 
        for (int i = 0; s[i]; i++) {
            int index = s[i] - 'a';
            while (p1 != root && p1->next[index] == NULL) {
                p1 = p1->fail;
            }
            p1 = p1->next[index];
            if (p1 == NULL) {
                p1 = root;
            }
            p2 = p1;
            while (p2 != root && p2->tail != -1) {
                cnt += p2->tail;
                p2->tail = -1;
                p2 = p2->fail;
            }
        }
        return cnt;
    }
    void clear(Tree* point) {
        for (int i = 0; i < SIZE; i++) {
            if (point->next[i] != NULL) {
                clear(point->next[i]);
            }
        }
        free(point);
    }
    int main() {
        int t, n;
        scanf("%d", &t);
        while (t--) {
            root = init();
            scanf("%d", &n);
            while (n--) {
                scanf("%s", p);
                insert(p);
            }
            scanf("%s", s);
            build();
            printf("%d
    ", query(s));
            clear(root);
            root = NULL;
        }
        return 0;
    } 

    build的时候网上的代码都是用自己写的队列,我一开始用自己写的队列运行时间达到了171ms,确实比库里封装的快不少,但是我还是喜欢用库里的比较保险。弄懂了KMP和字典树看一下网上的讲解就好理解了。

原文地址:https://www.cnblogs.com/Angel-Demon/p/10358416.html