题意:
给你很多个单词,然后给你一篇文章,问给出的单词在文章中出现的次数。
解析:
直接套用AC自动机的模板。
注意:
每个单词在目标串中出现的话,只能记为一次。
my code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxnode = (int)1e6 + 10;
const int sigma_size = 26;
struct AC {
int ch[maxnode][sigma_size];
int fail[maxnode];
int last[maxnode];
int val[maxnode];
int sz;
int ans;
void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); }
inline int idx(char c) { return c - 'a'; }
void insert(char *s) {
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u]++;
}
int find(char* tar) {
int n = strlen(tar);
int v = 0;
ans = 0;
for(int i = 0; i < n; i++) {
int c = idx(tar[i]);
while(v && !ch[v][c]) v = fail[v];
v = ch[v][c];
if(val[v]) print(v);
else if(last[v]) print(last[v]);
}
return ans;
}
void print(int j) {
while(j) {
ans += val[j]; //找到的话,将所有的val[j]都加进来
val[j] = 0; //然后将val[j]清空,之后就算有相同的单词也可以不用计算。
j = last[j];
}
}
int getFail() { //构造失配指针
queue<int> que;
//初始化队列
//root的子节点的失配指针都指向root。
for(int c = 0; c < sigma_size; c++) {
int u = ch[0][c];
if(u) { fail[u] = 0; que.push(u); last[u] = 0; }
}
//节点(字符为x)的失败指针指向:从x节点的父节点的fail节点回溯直到找到某节点的子节点也是字符x,没有找到就指向root。
//按照bfs顺序计算失配函数
while(!que.empty()) {
int r = que.front(); que.pop(); //取出队首,遍历当前的子节点
for(int c = 0; c < sigma_size; c++) {
int u = ch[r][c]; //当前的子节点
if(!u) continue; //且子节点不能为空
que.push(u); //将不为空的子节点存入队列
int v = fail[r]; //r = x 节点的父节点的fail节点
//从x节点的父节点的fail节点回溯,直到找到某节点的子节点也是字符x
//ch[v][c] == 0表示其子节点不是x,可以继续向上找;
//v != 0,fail[v]存在,表示还能向上找
while(v && !ch[v][c]) v = fail[v];
fail[u] = ch[v][c]; //找到某节点的子节点也是字符x,将u的失配指针指向最近的某节点的子节点也是字符x
last[u] = val[fail[u]] ? fail[u] : last[fail[u]];
}
}
}
} ac;
const int MAXN = (int)1e4 + 10;
char key[MAXN][55];
char tar[maxnode];
int n;
int main() {
int T;
scanf("%d", &T);
while(T--) {
ac.clear();
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%s", key[i]);
ac.insert(key[i]);
}
ac.getFail();
scanf("%s", tar);
printf("%d\n", ac.find(tar));
}
return 0;
}