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;
}