【题目大意】给出多组数据(第一行输入的N),每组数据第一行给出要录入的单词数,最后一行给出文章,要求计算录入的单词在文章中出现了多少次。
【分析】很裸的AC自动机。首先建一棵Trie树,再用类似于KMP的next数组一样对每一个Trie树结点求出对应的失败指针,从而在每次失败后,在每个录入单词中找一个最优的串的合适的位置继续进行匹配,最后对文章进行匹配即可。
→为方便查阅,给出链接:
Trie树百度百科:http://baike.baidu.com/view/1436495.htm
KMP详解:http://blog.csdn.net/u011400953/article/details/9324095
【原题地址】http://acm.hdu.edu.cn/showproblem.php?pid=2222
【代码】
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAX 500001
struct TREE{int fail,son[26+6],num;};
TREE trie[MAX];
int CASE,N,node_num=0;
int ans=0;
char NOW1[101],NOW2[1000001];
void tree_clear()
{
ans=0;
node_num=0;
for(int i=0;i<=N;i++)
{
trie[i].fail=0;
trie[i].num=0;
for(int j=0;j<=26;j++)
trie[i].son[j]=0;
}
}
void insect(char *NOW)
{
int now=0,len=strlen(NOW);
for(int i=0;i<len;i++)
{
if(!trie[now].son[(int)NOW[i]-(int)'a'])
trie[now].son[(int)NOW[i]-(int)'a']=++node_num;
now=trie[now].son[(int)NOW[i]-(int)'a'];
}
trie[now].num++;//=1 ?
}
void build_fail()
{
queue<int> q;
int now=0;
for(int i=0;i<26;i++)
if(trie[now].son[i])
{
q.push(trie[now].son[i]);
trie[trie[now].son[i]].fail=0;
}
while(!q.empty())
{
int point,now_fail;
now=q.front();
q.pop();
for(int i=0;i<26;i++)
if(point=trie[now].son[i])
{
now_fail=trie[now].fail;
while(now_fail && !trie[now_fail].son[i])
now_fail=trie[now_fail].fail;
trie[point].fail=trie[now_fail].son[i];
q.push(point);
}
}
}
void find(char *NOW)
{
int len=strlen(NOW),now=0,use=0;
for(int i=0;i<len;i++)
{
while(now && !trie[now].son[(int)NOW[i]-(int)'a'])
now=trie[now].fail;
now=trie[now].son[(int)NOW[i]-(int)'a'];
use=now;
while(use)
{
ans+=trie[use].num;
trie[use].num=0;
use=trie[use].fail;
}
}
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
scanf("%d",&CASE);
for(int i=1;i<=CASE;i++)
{
scanf("%d",&N);
tree_clear();
for(int j=1;j<=N;j++)
{
scanf("%s",NOW1);
insect(NOW1);
}
build_fail();
scanf("%s",NOW2);
find(NOW2);
printf("%d\n",ans);
}
return 0;
}