什么是AC自动机?

百度百科

Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。

要学会AC自动机,我们必须知道什么是Trie,也就是字典树。Trie树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

AC自动机有什么作用?

快速寻找多个字串与原串的关系,是多个字符串的kmp匹配算法

怎么实现AC自动机?

①trie字典树部分

这里只需要进行一个insert函数书写即可,把每个字串插入到trie树中,不同于字典树模板,这个insert函数需要记录词尾。

具体代码

void insert(string a)
{
int rt=0;
for(int i=0;i<a.size();i++)
{
int id=a[i]-'a';
if(!trie[rt][id])
trie[rt][id]=++cnt;
rt=trie[rt][id];
}
bk[rt]++;
}

②KMP部分

build函数

①枚举根节点的各个孩子如果存在就把他们入队

queue<int> q;
memset(fail,0,sizeof(fail));
for(int i=0;i<26;i++)
if(trie[0][i])
q.push(trie[0][i]);

②BFS进行搜索

转载于洛谷日报第44期

下面介绍构建fail指针的基础思想:(强调!基础思想!基础!)

构建fail指针,可以参考KMP中构造next数组的思想。

我们利用部分已经求出fail指针的结点推导出当前结点的fail指针。具体我们用BFS实现:

考虑字典树中当前的节点u,u的父节点是p,p通过字符c的边指向u。

假设深度小于u的所有节点的fail指针都已求得。那么p的fail指针显然也已求得。

我们跳转到p的fail指针指向的结点fail[p];

如果结点fail[p]通过字母cc连接到的子结点w存在:

则让u的fail指针指向这个结点w(fail[u]=w)。

相当于在pp和fail[p]后面加一个字符cc,就构成了fail[u]。

如果fail[p]通过字母cc连接到的子结点w不存在:

那么我们继续找到fail[fail[p]]指针指向的结点,重复上述判断过程,一直跳fail指针直到根节点。

如果真的没有,就令fail[u]=根节点。

如此即完成了fail指针的构建。

while(!q.empty())
{
int k=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(trie[k][i])
{
fail[trie[k][i]]=trie[fail[k]][i];
q.push(trie[k][i]);
}
else
trie[k][i]=trie[fail[k]][i];
}
}

完整代码

void build()
{
queue<int> q;
memset(fail,0,sizeof(fail));
for(int i=0;i<26;i++)
if(trie[0][i])
q.push(trie[0][i]);
while(!q.empty())
{
int k=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(trie[k][i])
{
fail[trie[k][i]]=trie[fail[k]][i];
q.push(trie[k][i]);
}
else
trie[k][i]=trie[fail[k]][i];
}
}
}

个人的理解是,这一部分首先把根的子节点入队,然后bfs建立字典图,感觉跟并查集有点类似

③查询部分

这里没有什么好说的,先和并查集的感觉,找到最后的叶子节点然后就是一顿查询fail,这里的查询是查询字串总共有多少个出现在了原串中,所以要bk[j]=-1,然后判断条件还得加上bk[j]!=-1

int search(string a)
{
int rt=0,ans=0;
for(int i=0;i<a.size();i++)
{
rt=trie[rt][a[i]-'a'];
for(int j=rt;j&&bk[j]!=-1;j=fail[j])
ans+=bk[j],bk[j]=-1;
}
return ans;
}

完整代码(洛谷 P3808 【模板】AC自动机(简单版))

#include <bits/stdc++.h>
using namespace std;
int trie[10000000][30];
int bk[10000000];
int fail[10000000];
int cnt;
void insert(string a)
{
int rt=0;
for(int i=0;i<a.size();i++)
{
int id=a[i]-'a';
if(!trie[rt][id])
trie[rt][id]=++cnt;
rt=trie[rt][id];
}
bk[rt]++;
}
void build()
{
queue<int> q;
memset(fail,0,sizeof(fail));
for(int i=0;i<26;i++)
if(trie[0][i])
q.push(trie[0][i]);
while(!q.empty())
{
int k=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(trie[k][i])
{
fail[trie[k][i]]=trie[fail[k]][i];
q.push(trie[k][i]);
}
else
trie[k][i]=trie[fail[k]][i];
}
}
}
int search(string a)
{
int rt=0,ans=0;
for(int i=0;i<a.size();i++)
{
rt=trie[rt][a[i]-'a'];
for(int j=rt;j&&bk[j]!=-1;j=fail[j])
ans+=bk[j],bk[j]=-1;
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
while(n--)
{
string a;
cin>>a;
insert(a);
}
build();
string a;
cin>>a;
cout<<search(a);
}

最新文章

  1. 例子:RSS Reader Sample
  2. easyui tabs update后tab上关闭图标失效的解决方案
  3. OBD K线抓包
  4. Java Web基础:JSP基础概念
  5. 再论EM算法的收敛性和K-Means的收敛性
  6. storm的数据源编程单元Spout学习整理
  7. POJ 3349 Snowflake Snow Snowflakes(哈希)
  8. Eclipse Android源代码新下载方法及关联
  9. vtp——vlan trunk protcal
  10. freemarker序列的拆分
  11. 共享AFHTTPSessionManager 单例好处浅析
  12. jvm007 jvm知识点总览
  13. 企业自主可控免费开源ERP:Odoo采购管理解决方案
  14. 机器学习基石笔记:09 Linear Regression
  15. 解决Android SDK下载和更新失败问题
  16. 电商项目中学到的git命令
  17. FastAdmin 插件刷新缓存出现 200 红色提示框解决 always_populate_raw_post_data
  18. Kafka设计解析(十)Kafka如何创建topic
  19. 编译geth报错的解决方法 make: *** [geth] 错误 1
  20. spring-integration-kafka

热门文章

  1. CANopen——总线基本知识
  2. BaezaYates 交集python和golang代码
  3. 从csv文件读取数据到二维vector
  4. bzoj4264
  5. uva11149
  6. 疫情控制 2012年NOIP全国联赛提高组(二分答案+贪心)
  7. curl 做爬虫 用服务器代理ip
  8. JQ 获取Table的td 值
  9. pyCharm最新激活码(2018)
  10. [转]mysql的查询、子查询及连接查询