AC自动机
以前还真认为AC自动机就是自动AC机
AC自动机,就是说,多模式匹配串,更像是一种树上KMP,在学这个之前有一些前置技能:KMP,字典树。
想学AC自动机,字典树是必须要会的,因为它就是在字典树上进行操作,但是它比字典树多了一个失配指针(fail指针)。本来我们要对一些的串进行匹配,那么我们暴力要一个一个去匹配,每个串都要从头开始匹配,那样的话,太浪费时间了,但是加上了fail指针之后,就直接开始一遍过就可以匹配所有的模式串(其实我感觉,AC自动机的fail指针就像是KMP的next[]数组在当前算法一样的重要地位)理解了fail指针,AC自动机也就理解了。
AC自动机三步走
1.
用字典树建图,对模式串建图,用字典树把模式串全部存起来。
2.
构建图中的fail指针。
1).用BFS进行进行层次遍历,先把root节点压入队列。(root的节点的fail为0)
2).对队列里面的结点进行出队操作,然后以次遍历这一层26个字母,如果有在树上的,那么我们通过它的父亲的fail节点,来判断当前节点的fail指针。
如果它的父亲节点的fail指针指向root节点,那么儿子的fail指针也将指向根节点。
如果它的父亲节点fail指针不为0,那么就找它指向的那个节点,判断它的树下有没有当前节点的字母存在,存在的话,当前节点的fail指针就指向这个字母的编号,不存在就一直向上找,一直找到存在的或者root节点,然后退出循环。
3).压入队列。
4).若不存在这个字母节点,那么主串就直接跟着父节点的fail指针往下继续找。
3.
主串查询操作
1).按串开始按位遍历。
2).每找到一个数,先判断这个数是否存在,要么是root节点,不然就跟着fail指针往上找。
3).找到符合要求的之后,判断当前节点是否作为单词的根节点,有的话则num+=tree[x].numm(当前这个节点的有多少个单词以此结尾)。
HDU—2222 题目传送门
题目大意
给你n个字符串,让再给你一条很长的主串,问这n个字符串中有多少个在这个主串里出现过。
思路
AC自动机入门模板题,
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e6+10;
struct zxc
{
int fail;
int cz;
int a[30];
}tree[3*N];
char sl[3*N];
char s[55];
int num;
void init(int x)
{
for(int i=0;i<=26;i++)
{
tree[x].a[i]=-1;
}
tree[x].fail=-1;
tree[x].cz=0;
}
void build(char z[])
{
int u=0;
int l=strlen(z);
for(int i=0;i<l;i++)
{
int c=z[i]-'a';
if(tree[u].a[c]==-1)
{
tree[u].a[c]=num++;
u=tree[u].a[c];
init(u);
}
else
{
u=tree[u].a[c];
}
}
tree[u].cz++;
}
void ACmadefail()
{
queue<int>q;
tree[0].fail=-1;
q.push(0);
while(!q.empty())
{
int p;
int u=q.front();
q.pop();
for(int i=0;i<26;i++)
{
int v=tree[u].a[i];
if(v>=0)
{
if(u==0)
{
tree[v].fail=0;
}
else
{
p=tree[u].fail;
while(p>=0)
{
if(tree[p].a[i]>=0)
{
tree[v].fail=tree[p].a[i];
break;
}
p=tree[p].fail;
}
if(p<0)
{
tree[v].fail=0;
}
}
q.push(v);
}
else
{
tree[u].a[i]=tree[tree[u].fail].a[i];
}
}
}
}
int ACslary()
{
int num=0;
int p=0,x;
int i=0;
while(sl[i])
{
int j=sl[i]-'a';
while(tree[p].a[j]<=0&&p)p=tree[p].fail;
p=tree[p].a[j];
if(p<=0)p=0;
x=p;
while(x&&tree[x].cz!=0)
{
num+=tree[x].cz;
tree[x].cz=0;
x=tree[x].fail;
}
i++;
}
return num;
}
int n;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init(0);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
build(s);
}
scanf("%s",sl);
ACmadefail();
printf("%d\n",ACslary());
}
return 0;
}