刚开始听AC自动机的时候,脑子里都是浆糊了。经过学长多次讲解,终于理解的差不多了。
HDU2222-Keywords Search:http://acm.hdu.edu.cn/showproblem.php?pid=2222
题意:先是输入一个T,代表有T组测试数据。每组数据以N开头,接下来有N个单词输入。最后再输入一段文章,文章只由小写字母组成。算出N个单词里面共有多少个在文章里出现过。注意单词是可以有重合部分的,如我输入a,ab,文章是abc,算有两个出现过。而且,重复出现的单词会被重复记录,如输入AA,AA,文章是AAB,还是算两个。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
struct Trie{
int Next[500010][26],Fail[500010],End[500010];//Next[now][buf[i]-'a']是now节点存着buf[i]字符的子节点的编号
int L,root;//注意这是全局变量,L是编号,root是根节点
int NewNode()//建立一个新的节点
{
for(int i=0;i<26;i++)//26叉树
Next[L][i] = -1;
End[L++] = 0;
return L-1;//返回节点编号 L-1
}
void Initial()
{
L = 0;
root = NewNode();
}
void Insert(char buf[])
{
int len=strlen(buf);
int now = root;
for(int i=0;i<len;i++)
{
if(Next[now][buf[i]-'a'] == -1)
Next[now][buf[i]-'a'] = NewNode();//若在子节点没有buf[i],则插入buf[i]
now = Next[now][buf[i]-'a'];//now是当前节点编号
}
End[now]++;//作为结束字符的编号加一,这也是为什么会重复计算同一个单词出现的次数
}
void Build()//建立失败指针
{
queue<int>ans;//用广搜,需要队列
Fail[root] = root;//根节点的失败指针指向自己
for(int i=0;i<26;i++)//情况一:父节点就是根节点的时候
{
if(Next[root][i] == -1)
Next[root][i]=root;
else{
Fail[Next[root][i]] = root;
ans.push(Next[root][i]);
}
}
while(!ans.empty())//情况二:当其父节点不是根节点的时候
{
int now = ans.front();
ans.pop();
for(int i=0;i<26;i++)
{
if(Next[now][i] == -1)
Next[now][i] = Next[Fail[now]][i];//若buf[i]没有插入字典树,则将令其等于其父节点的失败指针指向的节点的buf[i]字符所在的节点(有点绕,多理解几遍就可以了)
else{
Fail[Next[now][i]] = Next[Fail[now]][i];//若已经插入,其失败指针就指向其父节点的失败指针指向的节点的子节点buf[i]所在的节点
ans.push(Next[now][i]);//将其插入队列
}
}
}
}
int Query(char buf[])//这里传入的是文章
{
int now = root;//总是从根节点开始,now初始化为root
int res = 0;//结果
int len = strlen(buf);
for(int i = 0;i < len;i++)
{
now = Next[now][buf[i]-'a'];//当前节点的编号
int temp = now;//中间变量
while(temp != root)
{
res += End[temp];//只有找到单词最后一个字符所在的位置End[temp]才会是1,否则全为0,所以直接加
End[temp] = 0;//加完后置为0
temp = Fail[temp];//拓展到其失败指针指向的位置,继续查找
}
}
return res;
}
};
const int MAXN = 1000010;
int T,n;
char buf[MAXN];
Trie AC;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
AC.Initial();
for(int i = 0;i < n;i++)
{
scanf("%s",buf);
AC.Insert(buf);
}
AC.Build();
scanf("%s",buf);
printf("%d\n",AC.Query(buf));
}
return 0;
}