problem
- 给定n(< 1e4)个长度不超过50的单词(模式串)和一篇长为m(< 1e6)的文章(主串)。
- 求多少个单词在文章中出现了。
solution
- AC自动机模板
1、将所有模式串构建成字典树
2、求出nxt数组,其中nxt[i]表示匹配到i节点i的转移边都不能匹配了,满足最长前后缀关系的新节点。
3、继续匹配
codes
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 5e5+5;
int tot, tire[maxn][30], val[maxn], nxt[maxn];
void build(){
tot = 1;
memset(val,0,sizeof(val));
memset(tire,0,sizeof(tire));
}
void insert(char *s){
int u = 1, len = strlen(s);
for(int i = 0; i < len; i++){
int c = s[i]-'a';
if(tire[u][c]==0)tire[u][c] = ++tot;
u = tire[u][c];
}
val[u]++;
return ;
}
int find(char *s){
int ans = 0;
int u = 1, len = strlen(s);
for(int i = 0; i < len; i++){
int c = s[i]-'a';
int k = tire[u][c];
while(k > 1){
ans += val[k];
val[k] = 0;
k = nxt[k];
}
u = tire[u][c];
}
return ans;
}
void genfail(){
for(int i = 0; i < 26; i++)tire[0][i] = 1;
queue<int>q;
q.push(1); nxt[1] = 0;
while(q.size()){
int u = q.front(); q.pop();
for(int i = 0; i < 26; i++){
if(!tire[u][i])tire[u][i] = tire[nxt[u]][i];//如果不存在u->i转移边,就直接回到第一个满足字符i的节点
else{
q.push(tire[u][i]);
nxt[tire[u][i]] = tire[nxt[u]][i];
}
}
}
}
char s[maxn<<1];
int main(){
int T; scanf("%d",&T);
while(T--){
build();
int n; scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%s",s);
insert(s);
}
genfail();
scanf("%s",s);
printf("%d\n",find(s));
}
return 0;
}