1. 程式人生 > >hihoCoder Trie圖(AC自動機)

hihoCoder Trie圖(AC自動機)

原來AC自動機是Trie圖啊。。。 Trie圖與KMP的作用相似,都是求字串匹配,而且感覺求法也是類似,不過KMP是單個模式串求匹配,Trie圖是多個模式串求匹配,為了實現這個功能,Tire圖多了一個字首指標,類似KMP的失配陣列

首先按照Tire樹建立好一個樹,然後按照深度優先求取前面深度的字首指標,這個字首指標是根據最大字尾求得。 在這裡插入圖片描述 c字元後面的節點A的字首指向B’,因為abc的最大字尾和bc相同

查詢時,如果一個節點的子節點沒有後續的串,那麼就跳到他的字首指標,在查詢直至到根節點。

程式碼:

程式碼借鑑

typedef struct Trie{
        Trie *fail;
        Trie *
next[26]; int cnt; Trie() { memset(next, 0, sizeof(next)); fail = NULL; cnt = 0; } }TrieNode, *LinkTrie; LinkTrie root; int head, tail; void init() { root = new Trie(); head = tail = 0; } void insert(char *st) { LinkTrie p = root;
while(*st) { if(p->next[*st-'a'] == NULL) p->next[*st-'a'] = new Trie(); p = p->next[*st-'a']; st ++; } p->cnt++; } void build() { root->fail = NULL; deque<LinkTrie> q; q.push_back(root); while(!q.empty()) { LinkTrie tmp =
q.front(); LinkTrie p = NULL; q.pop_front(); for (int i = 0; i < 26; i ++) { if(tmp->next[i] != NULL) { if(tmp == root) tmp->next[i]->fail = root; else { p = tmp->fail; while(p != NULL) { if(p->next[i] != NULL) { tmp->next[i]->fail = p->next[i]; break; } p = p->fail; } if(p == NULL) tmp->next[i]->fail = root; } q.push_back(tmp->next[i]); } } } } int search(char *st) { int cnt = 0, t; LinkTrie p = root; while(*st) { t = *st - 'a'; while(p->next[t] == NULL && p != root) p = p->fail; p = p->next[t]; if(p == NULL) p = root; LinkTrie tmp = p; while(tmp != root && tmp->cnt != -1) {//一個模式串只被計算一次 cnt += tmp->cnt; tmp->cnt = -1; tmp = tmp->fail; } st ++; } return cnt; }

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

typedef struct Trie{
        Trie *fail;
        Trie *next[26];
        int cnt;
        Trie() {
            memset(next, 0, sizeof(next));
            fail = NULL;
            cnt = 0;
        }
}TrieNode, *LinkTrie;

LinkTrie root;
int head, tail;

void init() {
    root = new Trie();
    head = tail = 0;
}

void insert(char *st) {
    LinkTrie p = root;
    while(*st) {
        if(p->next[*st-'a'] == NULL)
            p->next[*st-'a'] = new Trie();
        p = p->next[*st-'a'];
        st ++;
    }
    p->cnt++;
}

void build() {
    root->fail = NULL;
    deque<LinkTrie> q;
    q.push_back(root);
    while(!q.empty()) {
        LinkTrie tmp = q.front();
        LinkTrie p = NULL;
        q.pop_front();
        for (int i = 0; i < 26; i ++) {
            if(tmp->next[i] != NULL) {
                if(tmp == root) tmp->next[i]->fail = root;
                else {
                    p = tmp->fail;
                    while(p != NULL) {
                        if(p->next[i] != NULL) {
                            tmp->next[i]->fail = p->next[i];
                            break;
                        }
                        p = p->fail;
                    }
                    if(p == NULL) tmp->next[i]->fail = root;
                }
                q.push_back(tmp->next[i]);
            }
        }
    }
}

int search(char *st) {
    int cnt = 0, t;
    LinkTrie p = root;
    while(*st) {
        t = *st - 'a';
        while(p->next[t] == NULL && p != root)
            p = p->fail;
        p = p->next[t];
        if(p == NULL) p = root;
        LinkTrie tmp = p;
        while(tmp != root && tmp->cnt != -1) {//一個模式串只被計算一次
            cnt += tmp->cnt;
            tmp->cnt = -1;
            tmp = tmp->fail;
        }
        st ++;
    }
    return cnt;
}

int main(int argc, char const *argv[]) {
    init();
    char s[1000005];
    int n;
    scanf("%d", &n);
    while(n --) {
        scanf("%s", s);
        insert(s);
    }
    build();
    scanf("%s", s);
    printf("%s\n", search(s) ? "YES" : "NO");
    return 0;
}