AC自动机是KMP算法和Trie(字典树)的巧妙结合这篇文章主要讲针对几个例题给出解答模版(主要是知识点自己讲不清楚)。

至于针对的知识点,给上几个我认为说的比较好的传送门,读者可以自行选择阅读。(我就是看这几个大佬文章学的)

KMP :             https://blog.csdn.net/v_JULY_v/article/details/7041827?spm=1001.2014.3001.5502

Trie(字典树) :        https://blog.csdn.net/m0_46202073/article/details/107253959?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164812432816780264091418%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164812432816780264091418&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-107253959.142^v3^pc_search_result_control_group,143^v4^control&utm_term=%E5%AD%97%E5%85%B8%E6%A0%91&spm=1018.2226.3001.4187

AC自动机原版 :

https://www.cnblogs.com/cmmdc/p/7337611.html

https://www.cnblogs.com/sclbgw7/p/9260756.html

AC自动机的last优化 :

https://www.cnblogs.com/sclbgw7/p/9875671.html

感谢以上大佬给出的解答%%%%。

下面我们针对几道模板例题进行解答。

P3808 【模板】AC 自动机(简单版)

https://www.luogu.com.cn/problem/P3808

本体是AC自动机的原始版,要求统计有多少模式串是文本串中出现过,代码如下

#include<bits/stdc++.h>
using namespace std;//一些习惯的简化
#define el '\n'
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define lop(i, a, b) for(int i = (a); i <  (b); i++)
#define dwn(i, a, b) for(int i = (a); i >= (b); i--)
#define ms(a, b) memset(a, b, sizeof(a))typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL, LL> PLL;
const int MAXN = 1e7 + 1000;
const int INF = 0x3f3f3f3f;
const int SIZE = 26 + 10;
const LL LNF = 9223372036854775807;
int ch[MAXN][SIZE];     //用于储存字典树,代表一个字符的地址
bool vis[MAXN];         //最后计数时记录遍历访问情况
int val[MAXN];          //记录某模式串的结束结点
LL ans = 0;int idx(char c){ return c - 'a'; };//把字幕'a' - 'z' 转换为 0 - 25
//建立字典树
struct Trie{int pos;            //新的存储结点的位置Trie(){pos = 1; ms(ch[0], 0); ms(vis, false);};//创建新树的初始化void insert(string T){//插入字典树中int Tlen = T.length(), p = 0;lop(i, 0, Tlen){int x = idx(T[i]);if(!ch[p][x]){ms(ch[pos], 0);val[pos] = 0;ch[p][x] = pos++;}p = ch[p][x];}val[p]++;}
};
//Fail指针(用数组表示)的建立与last优化
int last[MAXN], fail[MAXN];
void getFail(){queue<int> q;fail[0] = 0;//根只能指向自己lop(x, 0, 26){//对于根直接相连的结点进行初始化int u = ch[0][x];if(u){//存在这个结点fail[u] = 0;last[u] = 0;q.push(u);}}//进入类似BFS环节while(!q.empty()){int u = q.front();q.pop();lop(x, 0, 26){int v = ch[u][x]; //v是u的子结点if(!v){//u没有这个子节点    last优化一ch[u][x] = ch[fail[u]][x];//这个u直接连向能匹配到x的fail指针的子节点 连不到则回到0continue;}q.push(v);//如果u有这个子结点,入队int w = fail[u];while(w && !ch[w][x]) w = fail[w];//更新fail[w]找到第一个子节点有v的点  last优化优化二fail[v] = ch[w][x];last[v] = val[fail[v]] ? fail[v] : last[fail[v]];}}
}
//正式AC自动机的查找过程
void CountNum(int j){//计数if(j && !vis[j]){ans += val[j];vis[j] = true;CountNum(last[j]);//如果j还能找到下一个结束节点,继续搜索}
}
void ACautoFind(string S){//自动机int Slen = S.length();int now = 0;//当前所在位置lop(i, 0, Slen){//遍历每个字符int x = idx(S[i]);now = ch[now][x];if(val[now])//now是结束节点CountNum(now);else if(last[now]){//通过now的last指针可以连到一个结束节点CountNum(last[now]);}}
}int main(){cin.tie(0);cout.tie(0);cin.sync_with_stdio(false);int n;string str;Trie trie;ans = 0;cin >> n;   //getchar() + getline(cin, str)过不了!!!rep(i, 1, n){cin >> str;trie.insert(str);   }getFail();cin >> str;ACautoFind(str);cout << ans << el;return 0;
}

P5357 【模板】AC 自动机(二次加强版)

https://www.luogu.com.cn/problem/P5357

作为上一题的加强版本,其实并没有过多的修改,原本因为统计个数所以设置了vis[]数组避免多次统计,而新的题目是要输出出现最多次的模式串,那么我们就应该统计出现次数,所以vis[]的含义从原来的是否出现变成了出现次数。而其次就是增加num[]数组用于标记每个结束结点对应的字符串编号(我们这里设为从1 ~ n)。在记录完以后再遍历输出即可,与第一版没太大差异,只需要改动输出和计数的细节。代码如下

#include<bits/stdc++.h>
using namespace std;//一些习惯的简化
#define el '\n'
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define lop(i, a, b) for(int i = (a); i <  (b); i++)
#define dwn(i, a, b) for(int i = (a); i >= (b); i--)
#define ms(a, b) memset(a, b, sizeof(a))typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL, LL> PLL;
const int MAXN = 1e7 + 1000;
const int INF = 0x3f3f3f3f;
const int SIZE = 26 + 10;
const LL LNF = 9223372036854775807;
int ch[MAXN][SIZE];     //用于储存字典树,代表一个字符的地址
int val[MAXN];          //记录某模式串的结束结点
//多加一个处理个数的数组
int cnt = 0;            //记录编号  从1开始
int num[MAXN];          //登记结束结点所对应的子串编号
int vis[MAXN];
LL ans = 0;int idx(char c){ return c - 'a'; };//把字幕'a' - 'z' 转换为 0 - 25
//建立字典树
struct Trie{int pos;            //新的存储结点的位置Trie(){     //创建新树的初始化cnt = 0; pos = 1; ms(ch[0], 0); ms(vis, 0); ms(num, 0);};  void insert(string T){//插入字典树中int Tlen = T.length(), p = 0;lop(i, 0, Tlen){int x = idx(T[i]);if(!ch[p][x]){ms(ch[pos], 0);val[pos] = 0;ch[p][x] = pos++;}p = ch[p][x];}val[p]++;num[p] = (++cnt);}
};
//Fail指针(用数组表示)的建立与last优化
int last[MAXN], fail[MAXN];
void getFail(){queue<int> q;fail[0] = 0;//根只能指向自己lop(x, 0, 26){//对于根直接相连的结点进行初始化int u = ch[0][x];if(u){//存在这个结点fail[u] = 0;last[u] = 0;q.push(u);}}//进入类似BFS环节while(!q.empty()){int u = q.front();q.pop();lop(x, 0, 26){int v = ch[u][x]; //v是u的子结点if(!v){//u没有这个子节点    last优化一ch[u][x] = ch[fail[u]][x];//这个u直接连向能匹配到x的fail指针的子节点 连不到则回到0continue;}q.push(v);//如果u有这个子结点,入队int w = fail[u];while(w && !ch[w][x]) w = fail[w];//更新fail[w]找到第一个子节点有v的点  last优化优化二fail[v] = ch[w][x];last[v] = val[fail[v]] ? fail[v] : last[fail[v]];}}
}
//正式AC自动机的查找过程
void CountNum(int j){//计数if(j){vis[num[j]]++;CountNum(last[j]);//如果j还能找到下一个结束节点,继续搜索}
}
void ACautoFind(string S){//自动机int Slen = S.length();int now = 0;//当前所在位置lop(i, 0, Slen){//遍历每个字符int x = idx(S[i]);now = ch[now][x];if(val[now])//now是结束节点CountNum(now);else if(last[now]){//通过now的last指针可以连到一个结束节点CountNum(last[now]);}}
}int main(){cin.tie(0);cout.tie(0);cin.sync_with_stdio(false);int n;while(cin >> n && n){   //getchar() + getline(cin, str)过不了!!!string str[200], T;Trie trie;ans = 0;rep(i, 1, n){cin >> str[i];trie.insert(str[i]);   }getFail();cin >> T;   //文本串ACautoFind(T);int maxnum = -1;rep(i, 1, n){maxnum = max(maxnum, vis[i]);}cout << maxnum << el;rep(i, 1, n){if(vis[i] == maxnum)cout << str[i] << el;}}return 0;
}

P5357 【模板】AC 自动机(二次加强版)

https://www.luogu.com.cn/problem/P5357

对于这第三个版本我开始看感觉好像比第二个还容易,甚至就直接每个输出就完事了,然后看了下数据,明显数据量加大了不少。于是用原本的代码提交了一发。结果肯定是过不了的(交的时候其实也就猜到了,不然怎么可能叫二次加强呢)

并且我注意到这题从蓝题变成紫题,相比没那么简单。

根据题目说给的数据,我们可以发现字典树的最大长度远数组可容纳的范围。于是我就止步于此了(菜鸡就是我,水平就是低)。

如果还要深入学习的大佬可以继续研究一下,大概是优化思路有

拓扑建图优化  树上差分 领接表优化空间存储 

当然你也可以或直接改用后缀数组写法(以后可能会讲到)

AC自动机从入门到last优化相关推荐

  1. AC自动机 从入门到模板

    前言 作为一名菜鸡选手,我的目的仅仅是初步了解ac自动机的原理和各种性质... 正篇 1.AC自动机的基本性质 AC自动机的前置技能点 KMP,字典树 ac自动机的原理解释可以参考 hihocode ...

  2. HDU 6208 The Dominator of Strings AC自动机

    题目链接:HDU 6208 The Dominator of Strings Time Limit: 3000/3000 MS (Java/Others)    Memory Limit: 65535 ...

  3. 【uva11019-Matrix Matcher】AC自动机+优化+记录

    http://acm.hust.edu.cn/vjudge/problem/33057 题意:在二维文本串T中查找一个二维模板串P出现了多少次. 题解: 拆分模板串P的每一行,建AC自动机. 拆分文本 ...

  4. hdu 3962(AC自动机+矩阵优化dp)

    转载标记处:http://blog.csdn.net/woshi250hua/article/details/7599472 题目大意:给定m个DNA病毒序列,求碱基构成的长度为n且含有两个以上DNA ...

  5. HDOJ 2896 病毒侵袭(AC自动机入门)

    题意: 求感染病毒的网站,并输出其感染的病毒特征码编号. 思路: AC自动机入门,思路同 HDOJ 2222 #include <iostream> #include <deque& ...

  6. ac自动机(模板+一点点小优化)

    题意:给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. 题解:ac自动机+加一点点优化,否则过不了,具体见代码,就一行代码. 传送门 #include<iostream> # ...

  7. 【AC自动机】【数据结构】【树】【Aho-Corasick automation】AC自动机理解(入门)...

    引入 我们首先提出一个问题: 给出n个串每个串的长度≤m 然后给出一个长度为k的串,询问前n个串中有多少个是匹配成了的 暴力搜索 这题不是sb题目吗? 随随便便O(kmn)跑过. .... n=100 ...

  8. P7456 [CERC2018] The ABCD Murderer (ac自动机+线段树优化dp/反向st)

    做法 ac自动机预处理出最长后缀 f[i]=max(f[i-1],f[i-2],-,f[i-最长后缀])+1; 转移即可 下面有一种反向st维护的学到了 #include <bits/stdc+ ...

  9. java ac自动机_AC自动机算法详细讲解(入门)

    AC自动机讲解 序言:经常听别人说AC自动机,觉得ac自动机是个很神奇,很高深,很难的算法,学完之后发现,ac自动机确实很神奇,很高深,但是却并不难,只要知道怎么构建失败指针就基本初步掌握了. 先了解 ...

最新文章

  1. 图文详解什么是快速排序
  2. 原 CNCF 执行董事 Dan Kohn 辞世,沉痛哀悼
  3. es安装ik后报错无法启动 read write
  4. booloader编写
  5. MySQL逻辑物理备份测试
  6. java打包拆包_[Java] Java 打包成jar包 和 解压jar包
  7. HDU 4990 Ordered Subsequence --数据结构优化DP
  8. vmware workstation 关于三种网络连接方式的理解
  9. Java 数据结构与算法面试 链表
  10. JumpList中Recent类别和自定义类型
  11. QImage、QPainter绘图
  12. 基于联合非负矩阵分解的多视角聚类学习笔记
  13. 自学是一门手艺 python_《自学是门手艺》:Python自学指南(内附PPT)
  14. 基于HTML、CSS、JavaScript、jQuery的app小项目--简易备忘录
  15. java程序员生日祝福语_给朋友过生日的祝福语
  16. 关于mac上的所有东西都变小了
  17. HTML:如何设置网页标题上的图标
  18. 云服务器云虚拟主机区别,带你了解云服务器和云虚拟主机有什么区别?
  19. QuickBooks profitandloss report 获取Not Specified 详情
  20. @Value(“#{}“) 与 @Value(“${}“) 的区别

热门文章

  1. 矿渣玩客云刷机armbian教程及使用(新手上路篇)
  2. 亚马逊AI炒掉900名员工:这个时代正在惩罚混日子的人
  3. 源代码防泄密,SDC沙盒安全性如何?
  4. 8月第1周业务风控关注 |苹果App商店、iMessage被×××、×××信息“轰炸”
  5. TensorFlow和keras安装教程
  6. DEDE97_响应式仿WE手游中心游戏下载类网站织梦模板(自适应手机端)
  7. ES6--》了解并应用迭代器与生成器
  8. vue 后台返回的数据怎么换行 ?
  9. 什么是TypeReference?
  10. JavaEE——Spring学习笔记01【Ioc开发的模式】