http://acm.hdu.edu.cn/showproblem.php?pid=2222
题意:给你一些单词,然后给你一段很长的话,问你话里有多少个之前给你的那些单词(重复的只算一次)。
方法:AC自动机,即把trie树和kmp的失效匹配相结合。
AC代码如下:
#include <iostream>
#include <cstdio>
#include <string.h>
#include <queue>
using namespace std;
#define maxnode 500100
#define sigma 26
struct ac_automation{
int ch[maxnode][sigma];
// maxnode 一般设置为 模式串数量*模式串长度
//char数组里存的都是位置信息,甚至包含了一些失效指针的信息。
bool val[maxnode];
int cnt[maxnode]; // count 数量
int last[maxnode]; // 储存着最后在查询时需要向上层回溯的地址。
int f[maxnode]; // fail指针 储存着指向的地址
int sz; // the num of the trie
int ans; // answer
void clear(){ // 初始化
sz = 1;
ans = 0;
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
memset(cnt,0,sizeof(cnt));
}
int idx(char c){
return c - 'a';
}
void insert(char s[]/*,int v*/){ // 构建trie树
int u = 0;
for(int i = 0; s[i];i++){
int c = idx( s[i] );
if(!ch[u][c]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[u][c] = sz++;
}
u = ch[u][c];
}
cnt[u]++;
val[u] = 1; //v;
}
void build(){ // 构建失效指针
//注意:构建失效匹配的时候是按照bfs的思想来的
//就是一层一层的,即:下层的f[]和last[]是根据上层的数据来的
f[0] = 0;
queue<int> q;
for(int i = 0;i < sigma;i++){
if(ch[0][i]){
f[ch[0][i]] = 0; // 第一层的节点的fail指针应该指向root(0)
q.push(ch[0][i]);// 放进队列
last[ch[0][i]] = 0;//上一层绝对是root(0)
}
}
while(!q.empty()){
int now = q.front();
q.pop();
for(int i = 0;i < sigma ;i++){
int son = ch[now][i];
if(!son){
ch[now][i] = ch[f[now]][i];
// 提前将失效后指向此位置的的失效指针指向它应该指向的位置。
//因为这里找不到应有的字母,所以它应该等于自己的失效指针所指向的位置。
continue;
}
q.push(son);
f[son] = ch[f[now]][i];
last[son] = val[f[son]] ? f[son] : last[f[son]];
/* 如果此处的fail指针指向的位置是之前插入单词的结尾,
那么son节点的上一层(last)就为它的失效指针,
否则去找他的失效指针的的上一层(此处一定有一个有意义的值,因为上层已经确定)*/
}
}
}
void find(char *s){
int u = 0;
for(int i = 0;s[i];i++){
int c = idx(s[i]);
u = ch[u][c];
if(val[u]){
print(u);
}
else{
print(last[u]);
}
}
}
void print(int u){
if(u){
ans += cnt[u];
cnt[u] = 0; // 因为只是找出有多少个匹配的,计入ans后需要清零,避免重复记录。
print(last[u]);
}
}
}ac;
char s[60];
char text[1000100];
int main()
{
int ncase;
cin>>ncase;
while(ncase--){
int n;
cin>>n;
ac.clear();
for(int i = 0;i < n; i++){
scanf("%s",s);
ac.insert(s);
}
ac.build();
scanf("%s",text);
ac.find(text);
printf("%d\n",ac.ans);
}
return 0;
}