ps:学习AC自动机的时候必须先点亮KMP和字典树的技能点。
步骤:
1.将所有模式串建立一个字典树。
2.对字典树建立失配边。
3.用目标串与字典树进行匹配。
写法:
现在通用的AC自动机写法有两类:1.数组。2.指针。(也有把这两种结合到一起的写法)
指针比较直观,但是做有的题时不好操作;数组相对抽象些,但是适用范围广。我用的是数组的写法,借鉴的是kuangbin大神的板子。
例子:
举个例子,有模式串:she he say shr her
目标串:yasherhs
现在我们要用这5个模式串建立字典树,并构建失配边。如下图:
代码:(对AC自动机的解释都在代码注释中)
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <stdlib.h>
using namespace std;
const int maxn = 500010;
struct Trie{
//next存放的是字典树,在上图中是实线,next[i][j]中i是父亲节点,j是孩子节点
//fail存放失配边的信息,在上图中是蓝线
//end标记的是每个模式串末尾节点,在上图中用红色表示
int next[maxn][26],fail[maxn],end[maxn];
int root,L;//root是根节点,L是节点编号
//newnode()创建一个新的节点,并将它的所有孩子节点初始化为-1,返回这个新建节点的编号
int newnode(){
for(int i = 0;i < 26;i++)
next[L][i] = -1;
end[L++] = 0;
return L-1;
}
//初始化,给根节点初始化为0号节点
void init(){
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();
now = next[now][buf[i]-'a'];//切换到下一个节点
}
end[now]++;//标记末尾节点
}
//建立失配边,用bfs的方式建立
void build(){
queue<int> Q;
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;//如果没有前缀,就指向root就行了
Q.push(next[root][i]);
}
}
while(!Q.empty()){
int now = Q.front();
Q.pop();
for(int i = 0;i < 26;i++){
if(next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else{
fail[next[now][i]] = next[fail[now]][i];//失配边指向相同前缀
Q.push(next[now][i]);
}
}
}
}
//目标串与字典树进行匹配
int query(char buf[]){
int len = strlen(buf);
int now = root;
int res = 0;
for(int i = 0;i < len;i++){
now = next[now][buf[i]-'a'];
int temp = now;
//找目标串中与模式串相同的串
while(temp != root){
res += end[temp];
end[temp] = 0;//这里置零是防止重复计数
temp = fail[temp];
}
}
return res;//返回匹配成功的个数
}
//debug函数,不做多解释了
void dubug(){
}
};
char buf[1000010];
Trie ac;
int main()
{
int t;
int n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
ac.init();//初始化
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;
}