您的位置:首页 > 其它

HDU 3065 病毒侵袭持续中(AC自动机模板)

2015-04-30 15:14 399 查看
题目大意:输出每个模板串出现的次数。

AC自动机模板题。

AC自动机个人体会:

再了解AC自动机之前,建议先看看KMP和Trie树。

 

首先看Node结构体内的各个成员所代表的含义:

ch:children缩写,这个结点后的所有儿子。如果串1:abcde的d的编号为4,另外串2:abcdf的d也会编号为4(其实abcd四个都是编号一样的),那么d的儿子有e和f;

fail:该结点失配后,应该跳去下一个匹配的结点的编号。比如abcdf的f失配后,很大可能性跳去e(得看有多少个串了);

last:表示沿着该结点的fail走,遇到的第一个单词结尾的结点。

 

这里约定好当某结点的失配指针为root时,则下一个字符将会和根节点的儿子们比较(根节点为虚拟结点);当ch[i]为-1时,表示这个儿子不存在。

 

Insert函数和Trie树中的插入是一样的。

 

然而重头戏是Build函数,构造fail指针和last指针。

 

Build函数基本方法是BFS,当前层的结点构造完之后,再检查下一层的点。

 

初始结点为根节点,把根节点加入队列。根节点的fail指针和last指针均为-1;。

 

检查队列头的所有儿子,遍历字母集,如果当前结点有为该字母的儿子,则为该儿子结点构造fail和last指针。

如果父亲就是根节点,显然儿子的fail和last都应该指向root。

 

纪录父亲的fail指针,纪录fail指针为该字母的儿子的结点编号fc(不存在则为-1);在父亲不为根节点的情况下,儿子的fail指针应该指向fc(类似于KMP的next[i+1] = k+1),而last指针则是考虑fc是不是单词结点,如果不是,指向fc的last(类似于next[i+1] = next[k+1])。

 

如果这个儿子不存在,我们也可以建一个虚拟边,把这个儿子指向fail为这个字母的儿子,这样匹配的之后,就可以自动跳转了。记得检查父亲是不是root,不然会访问下标为-1的地方(所有可能)。

search的时候,相当于匹配,而对于每一个结点,都要回溯一边last指针,找出所有以该结点为单词结尾的模板串。

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
using namespace std;
#define MAXK 26
#define MAXN 50010
#define idx(c) (c - 'A')

struct Node
{
int ch[MAXK];
int fail, last, id;
void Init()
{
memset(ch, -1, sizeof(ch));
fail = last = -1;
id = 0;
}
};

struct Trie
{
Node next[MAXN];
int que[MAXN], head, tail;
int cnt[1010];
int root, sz;
void Init()
{
root = sz = 0;
next[root].Init();
memset(cnt, 0, sizeof(cnt));
}
void Insert(char *s, int k)
{
int u = root;
while(*s)
{
int c = idx(*s);
if(next[u].ch[c] == -1)
{
next[++sz].Init();
next[u].ch[c] = sz;
}
u = next[u].ch[c];
s++;
}
next[u].id = k;
}
void Build()
{
int u, ch, f, fc;
head = 0, tail = -1;
que[++tail] = root;
while(head <= tail)
{
u = que[head++];
for(int i = 0; i < MAXK; i++)
{
f = next[u].fail;
fc = next[f].ch[i];
if(next[u].ch[i] != -1)
{
ch = next[u].ch[i];
if(u == root) next[ch].fail = next[ch].last = root;
else
{
next[ch].fail = fc;
next[ch].last = (next[fc].id ? fc : next[fc].last);
if(next[ch].last == -1) next[ch].last = root;
}
que[++tail] = ch;
}
else
{
if(u == root) next[u].ch[i] = root;
else next[u].ch[i] = next[f].ch[i];
}
}
}
}
void Search(char *s)
{
int u = root;
while(*s)
{
if(*s >= 'A' && *s <= 'Z') u = next[u].ch[idx(*s)];
else u = -1;
if(u == -1) u = root;
int tmp = u;
while(tmp != root)
{
if(next[tmp].id) cnt[next[tmp].id]++;
tmp = next[tmp].last;
}
s++;
}
}
}ac;

char vir[1010][56];
char web[2000010];

int main()
{
//    freopen("3065.in", "r", stdin);
int n;
while(~scanf("%d", &n))
{
getchar();
ac.Init();
for(int i = 1; i <= n; i++)
{
//            scanf("%s", vir[i]);
gets(vir[i]);
ac.Insert(vir[i], i);
}
ac.Build();
//        scanf("%s", web);
gets(web);
ac.Search(web);
for(int i = 1; i <= n; i++) if(ac.cnt[i])
printf("%s: %d\n", vir[i], ac.cnt[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: