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