Trie圖-hihoCoder1036

Trie圖

Trie圖相當於在AC自動機上優化了一下(感覺像是AC自動機的進階版本),使匹配的速度更快。

在學Trie圖之前一定要先學會AC自動機:
http://blog.csdn.net/williamsun0122/article/details/75576970

Trie圖是跟着fail指針一起構建的,同樣,我們看圖理解。(以字符集爲{0,1}爲例)

首先,我們必須明確Trie圖就是把AC自動機裏建立的Trie樹上的所有節點的字符集指針都構建指向。以上圖爲例就是把所有節點的0,1指針都構建指向。

圖中綠色節點爲根節點,藍色節點爲中間節點,紅色節點爲終止節點。黑虛線爲節點fail指針指向,橙實線爲Trie圖構建中字符集指針指向。

(Trie圖的構建同樣是用BFS實現)
爲了方便描述,我們先把所有trie樹上的結點進行編號,編號順序爲結點的插入順序,根結點編號爲0。

最開始根節點入隊。
第一次循環,從根節點開始訪問所有字符集指針。因爲根節點的所有字符集指針都存在,所以直接入隊,所以1,3入隊。此時隊列爲{1,3}
第二次循環,從1號節點開始訪問所有字符集指針。此時1號節點的’0’字符的指針爲空即不存在,那麼我們就將其指向1號節點的fail指針指向的節點(即根節點)的’0’字符指針指向的節點(就是1號節點)(有點繞,仔細體會一下)。1號節點的’1’字符指針存在,所以把2號指針入隊,此時隊列爲{3,2}
之後一直這樣BFS即可,你可以自己試試,然後和圖對比一下看你想的對不對。(上圖1號節點的’1’字符指針的指向好像錯了,應該就是指向2號節點的)

還是不能理解的話看看代碼吧,結合代碼和圖理解一下。

void ac_setTrieGraph()
{
    node *p,*tmp;
    queue<node*> q;
    q.push(root);
    while(!q.empty())
    {
        p = q.front(),q.pop();
        for(int i=0;i<26;i++) //遍歷字符集,這裏是26個字母
        {
            if(p->next[i]==NULL)
            {
                //和AC自動機相比就是這裏改了一下
                if(p==root) p->next[i] = root;
                else p->next[i] = p->fail->next[i];
            }
            else
            {
                if(p==root)
                {
                    p->next[i]->fail = root;
                    q.push(p->next[i]);
                }
                else
                {
                    tmp = p->fail;
                    while(tmp)
                    {
                        if(tmp->next[i])
                        {
                            p->next[i]->fail = tmp->next[i];
                            break;
                        }
                        tmp = tmp->fail;
                    }
                    if(tmp==NULL) p->next[i]->fail = root;
                    q.push(p->next[i]);
                }
            }
        }
    }
}

爲什麼Trie圖快一些?我覺得是因爲它直接照着匹配串一個個字符去匹配,省去了AC自動機裏匹配時fail指針的遞歸過程。

參考博客:
http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html

這個大神很牛,一些算法我覺得講的都很清晰很好懂。

hihoCoder1036

基本上就是一道AC自動機的裸題,但是直接AC自動機會TLE,Trie圖就過了。因爲Trie圖就是在AC自動機的基礎上優化一點,所以我直接貼代碼。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6+5;

char word[maxn],str[maxn];

int n;

struct node{
    node *next[26];
    node *fail;
    int num;
    node()
    {
        for(int i=0;i<26;i++) next[i] = NULL;
        fail = NULL;
        num = 0;
    }
}*root;

void init()
{
    root = new node();
}

void ac_insert()
{
    node *p = root;
    for(int i=0;word[i];i++)
    {
        int id = word[i]-'a';
        if(p->next[id]==NULL) p->next[id] = new node;
        p = p->next[id];
    }
    p->num++;
}

void ac_setTrieGraph()
{
    node *p,*tmp;
    queue<node*> q;
    q.push(root);
    while(!q.empty())
    {
        p = q.front(),q.pop();
        for(int i=0;i<26;i++)
        {
            if(p->next[i]==NULL)
            {
                if(p==root) p->next[i] = root;
                else p->next[i] = p->fail->next[i];
            }
            else
            {
                if(p==root)
                {
                    p->next[i]->fail = root;
                    q.push(p->next[i]);
                }
                else
                {
                    tmp = p->fail;
                    while(tmp)
                    {
                        if(tmp->next[i])
                        {
                            p->next[i]->fail = tmp->next[i];
                            break;
                        }
                        tmp = tmp->fail;
                    }
                    if(tmp==NULL) p->next[i]->fail = root;
                    q.push(p->next[i]);
                }
            }
        }
    }
}

bool ac_automation()
{
    node *p = root;
    for(int i=0;str[i];i++)
    {
        int id = str[i]-'a';
        p = p->next[id];
        if(p->num>0) return true;
    }
    return false;
}

int main()
{
    scanf("%d",&n);
    init();
    while(n--)
    {
        scanf("%s",word);
        ac_insert();
    }
    ac_setTrieGraph();
    scanf("%s",str);
    if(ac_automation()) printf("YES\n");
    else printf("NO\n");
    return 0;
}
發佈了82 篇原創文章 · 獲贊 37 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章