在没学ac自动机之前,觉得ac自动机是个很神奇,很高深,很难的算法,学完之后发现,ac自动机确实很神奇,很高深,但是却并不难。
我说ac自动机很神奇,在于这个算法中失配指针的妙处(好比kmp算法中的next数组),说它高深,是因为这个不是一般的算法,而是建立在两个普通算法的基础之上,而这两个算法就是kmp与字典树。所以,如果在看这篇博客之前,你还不会字典树或者kmp算法,那么请先学习字典树或者kmp算法之后再来看这篇博客。好了,闲话扯完了,下面进入正题。
在学习一个新东西之前,一定要知道这个东西是什么,有什么用,我们学它的目的是什么,如果对这些东西没有一个清楚的把握,我不认为你能学好这个新知识。
那么首先我们来说一下ac自动机是什么。下面是我从百度上找的。Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。
从上面我们可以知道,ac自动机其实就是一种多模匹配算法,那么你可能会问什么叫做多模匹配算法。下面是我对多模匹配的理解,与多模与之对于的是单模,单模就是给你一个单词,然后给你一个字符串,问你这个单词是否在这个字符串中出现过(匹配),这个问题可以用kmp算法在比较高效的效率上完成这个任务。那么现在我们换个问题,给你很多个单词,然后给你一段字符串,问你有多少个单词在这个字符串中出现过,当然我们暴力做,用每一个单词对字符串做kmp,这样虽然理论上可行,但是时间复杂度非常之高,当单词的个数比较多并且字符串很长的情况下不能有效的解决这个问题,所以这时候就要用到我们的ac自动机算法了。
对于上面的文字,我已经回答了什么是多模匹配和我们为什么要学习ac自动机那就是ac自动机的作用是什么等一系列问题。下面是ac自动机的具体实现步骤以及模板代码。
1.把所有的单词建立一个字典树。
在建立字典树之前,我们先定义每个字典树上节点的结构体变量
struct node{
node *next[26];
node *fail;
int sum;
};
其中fail 是失配指针
sum是这个节点是不是一个单词的结尾,以及相应的个数。
下面是字典树的建立过程
void Insert(char *s)
{
node *p = root;
for(int i = 0; s[i]; i++)
{
int x = s[i] - 'a';
if(p->next[x] == NULL)
{
newnode=(struct node *)malloc(sizeof