AC自动机专题
AC自动机简介:KMP是用于解决单模式串匹配问题, AC自动机用于解决多模式串匹配问题。
精华:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字目也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
如果用KMP来解决多模式串匹配问题,则复杂度为O(n + k * m), 而AC自动机的负责度为O(n + m + z), z为模式串出现的次数。
学习链接:
http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d
http://blog.csdn.net/niushuai666/article/details/7002823
http://www.cnblogs.com/kuangbin/p/3164106.html
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2222
思路:AC自动机的入门题,用的是bin牛的模板,统计End数组即可,统计过的需要清0.
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <queue>
- #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
- #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
- using namespace std;
- const int MAX_N = ( + );
- struct Trie {
- int next[MAX_N][], End[MAX_N], fail[MAX_N];
- int root, L;
- int NewNode()
- {
- FOR(i, , ) next[L][i] = -;
- End[L++] = ;
- return L - ;
- }
- void Init()
- {
- L = ;
- root = NewNode();
- }
- void Insert(char *str)
- {
- int len = strlen(str), now = root;
- FOR(i, , len) {
- int id = str[i] - 'a';
- if (next[now][id] == -) next[now][id] = NewNode();
- now = next[now][id];
- }
- ++End[now];
- }
- void Build()
- {
- queue<int > que;
- fail[root] = root;
- FOR(i, , ) {
- if (next[root][i] == -) next[root][i] = root;
- else {
- fail[next[root][i]] = root;
- que.push(next[root][i]);
- }
- }
- while (!que.empty()) {
- int now = que.front();
- que.pop();
- FOR(i, , ) {
- if (next[now][i] == -) {
- next[now][i] = next[fail[now]][i];
- } else {
- fail[next[now][i]] = next[fail[now]][i];
- que.push(next[now][i]);
- }
- }
- }
- }
- int Query(char *str)
- {
- int len = strlen(str), now = root, res = ;
- FOR(i, , len) {
- int id = str[i] - 'a';
- now = next[now][id];
- int tmp = now;
- while (tmp != root) {
- res += End[tmp];
- End[tmp] = ;
- tmp = fail[tmp];
- }
- }
- return res;
- }
- } AC;
- int n;
- char str[ + ];
- int main()
- {
- int Cas;
- scanf("%d", &Cas);
- while (Cas--) {
- AC.Init();
- scanf("%d", &n);
- REP(i, , n) {
- scanf("%s", str);
- AC.Insert(str);
- }
- AC.Build();
- scanf("%s", str);
- printf("%d\n", AC.Query(str));
- }
- return ;
- }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896
思路:和上题差不多,只是用End数组来记录序号而已。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <queue>
- #include <vector>
- #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
- #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
- using namespace std;
- const int MAX_N = ( + );
- struct Trie {
- int next[MAX_N][], End[MAX_N], fail[MAX_N];
- int root, L;
- int NewNode() {
- FOR(i, , ) next[L][i] = -;
- End[L++] = ;
- return L - ;
- }
- void Init() {
- L = ;
- root = NewNode();
- }
- void Insert(char *str, int index) {
- int len = strlen(str), now = root;
- FOR(i, , len) {
- int id = str[i];
- if (next[now][id] == -) next[now][id] = NewNode();
- now = next[now][id];
- }
- End[now] = index;
- }
- void Build() {
- queue<int > que;
- fail[root] = root;
- FOR(i, , ) {
- if (next[root][i] == -) next[root][i] = root;
- else {
- fail[next[root][i]] = root;
- que.push(next[root][i]);
- }
- }
- while (!que.empty()) {
- int now = que.front();
- que.pop();
- FOR(i, , ) {
- if (next[now][i] == -) {
- next[now][i] = next[fail[now]][i];
- } else {
- fail[next[now][i]] = next[fail[now]][i];
- que.push(next[now][i]);
- }
- }
- }
- }
- void Query(char *str, vector<int > &ans) {
- int len = strlen(str), now = root;
- FOR(i, , len) {
- now = next[now][str[i]];
- int tmp = now;
- while (tmp != root) {
- if (End[tmp]) ans.push_back(End[tmp]);
- tmp = fail[tmp];
- }
- }
- }
- } AC;
- int N, M, res;
- char str[ + ];
- vector<int > ans[ + ];
- int main()
- {
- AC.Init();
- scanf("%d", &N);
- REP(i, , N) {
- scanf("%s", str);
- AC.Insert(str, i);
- }
- AC.Build();
- scanf("%d", &M);
- FOR(i, , M) {
- scanf("%s", str);
- AC.Query(str, ans[i]);
- }
- res = ;
- FOR(i, , M) {
- if ((int)ans[i].size()) {
- printf("web %d:", i + );
- sort(ans[i].begin(), ans[i].end());
- FOR(j, , (int)ans[i].size()) printf(" %d", ans[i][j]);
- puts("");
- ++res;
- }
- }
- printf("total: %d\n", res);
- return ;
- }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3065
思路:用一个数组来记录模式串在主串中出现的次数。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <queue>
- #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
- #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
- using namespace std;
- const int MAX_N = ( + );
- int N, num[ + ];
- char ss[ + ][];
- char str[ + ];
- struct Trie {
- int next[MAX_N][], End[MAX_N], fail[MAX_N];
- int root, L;
- int NewNode() {
- FOR(i, , ) next[L][i] = -;
- End[L++] = -;
- return L - ;
- }
- void Init() {
- L = ;
- root = NewNode();
- }
- void Insert(char *str, int index) {
- int len = strlen(str), now = root;
- FOR(i, , len) {
- if (next[now][str[i]] == -) next[now][str[i]] = NewNode();
- now = next[now][str[i]];
- }
- End[now] = index;
- }
- void Build() {
- queue<int > que;
- fail[root] = root;
- FOR(i, , ) {
- if (next[root][i] == -) next[root][i] = root;
- else {
- fail[next[root][i]] = root;
- que.push(next[root][i]);
- }
- }
- while (!que.empty()) {
- int now = que.front();
- que.pop();
- FOR(i, , ) {
- if (next[now][i] == -) next[now][i] = next[fail[now]][i];
- else {
- fail[next[now][i]] = next[fail[now]][i];
- que.push(next[now][i]);
- }
- }
- }
- }
- void Query(char *str) {
- memset(num, , sizeof(num));
- int len = strlen(str), now = root;
- FOR(i, , len) {
- now = next[now][str[i]];
- int tmp = now;
- while (tmp != root) {
- if (End[tmp] != -) ++num[End[tmp]];
- tmp = fail[tmp];
- }
- }
- FOR(i, , N) {
- if (num[i]) printf("%s: %d\n", ss[i], num[i]);
- }
- }
- } AC;
- int main()
- {
- while (~scanf("%d", &N)) {
- AC.Init();
- scanf("%d", &N);
- FOR(i, , N) {
- scanf("%s", ss[i]);
- AC.Insert(ss[i], i);
- }
- AC.Build();
- scanf("%s", str);
- AC.Query(str);
- }
- return ;
- }
题目链接:http://poj.org/problem?id=2778
思路:需要用到的知识:有向图中点A到点B走K步的路径数等于有向图原始矩阵的K次幂。然后对于已经建好的Trie图,我们就可以建图了,如果某个节点A不是终止节点并且这个节点的next节点B也不是终止节点,那么就连边(表示从A点走1步到节点B的方法有1种)。建好图之后就是矩阵的快速幂了,然后在统计节点0(根节点)到其余节点走N步的方法数的总和。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <queue>
- #define REP(i, a, b) for (int i = (a); i < (b); ++i)
- #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
- using namespace std;
- const int MAX_N = ( + );
- const int MOD = ();
- int M, N;
- char str[];
- struct Matrix {
- long long mat[MAX_N][MAX_N];
- int n;
- Matrix() {}
- Matrix(int _n)
- {
- n = _n;
- REP(i, , n)
- REP(j, , n) mat[i][j] = ;
- }
- Matrix operator *(const Matrix &b) const
- {
- Matrix c = Matrix(n);
- REP(i, , n) {
- REP(j, , n) {
- REP(k, , n) {
- c.mat[i][j] += mat[i][k] * b.mat[k][j];
- if (c.mat[i][j] >= MOD) c.mat[i][j] %= MOD;
- }
- }
- }
- return c;
- }
- };
- Matrix Pow(Matrix mat, int n)
- {
- Matrix ONE = Matrix(mat.n);
- REP(i, , mat.n) ONE.mat[i][i] = ;
- Matrix tmp = mat;
- while (n) {
- if (n & ) ONE = ONE * tmp;
- n >>= ;
- tmp = tmp * tmp;
- }
- return ONE;
- }
- struct Trie {
- int next[MAX_N][], End[MAX_N], fail[MAX_N];
- int L, root;
- int NewNode()
- {
- REP(i, , ) next[L][i] = -;
- End[L++] = ;
- return L - ;
- }
- void Init()
- {
- L = ;
- root = NewNode();
- }
- int getID(char ch)
- {
- if (ch == 'A') return ;
- if (ch == 'C') return ;
- if (ch == 'G') return ;
- if (ch == 'T') return ;
- }
- void Insert(char *str)
- {
- int len = strlen(str), now = root;
- REP(i, , len) {
- int id = getID(str[i]);
- if (next[now][id] == -) next[now][id] = NewNode();
- now = next[now][id];
- }
- End[now] = ;
- }
- void Build()
- {
- queue<int > que;
- fail[root] = root;
- REP(i ,, ) {
- if (next[root][i] == -) next[root][i] = root;
- else {
- fail[next[root][i]] = root;
- que.push(next[root][i]);
- }
- }
- while (!que.empty()) {
- int now = que.front();
- que.pop();
- if (End[fail[now]]) End[now] = ;
- REP(i, , ) {
- if (next[now][i] == -) next[now][i] = next[fail[now]][i];
- else {
- fail[next[now][i]] = next[fail[now]][i];
- que.push(next[now][i]);
- }
- }
- }
- }
- Matrix getMatrix()
- {
- Matrix res = Matrix(L);
- REP(i, , L)
- REP(j, , ) if (!End[next[i][j]]) ++res.mat[i][next[i][j]];
- return res;
- }
- } AC;
- int main()
- {
- while (~scanf("%d %d", &M, &N)) {
- AC.Init();
- FOR(i, , M) scanf("%s", str), AC.Insert(str);
- AC.Build();
- Matrix tmp = AC.getMatrix();
- tmp = Pow(tmp, N);
- long long ans = ;
- REP(i, , tmp.n) {
- ans += tmp.mat[][i];
- if (ans >= MOD) ans %= MOD;
- }
- printf("%lld\n", ans);
- }
- return ;
- }
AC自动机专题的更多相关文章
- AC自动机专题总结
最近学习了AC自动机,做了notonlysuccess大牛里面的题,也该来个总结了. AC自动机(Aho-Corasick Automaton)在1975年产生于贝尔实验室,是著名的多模匹配算法之一. ...
- AC自动机 专题
// 求目标串中出现了几个模式串 //==================== #include <stdio.h> #include <algorithm> #include ...
- 【原创】AC自动机小结
有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配. AC自动机 其实 就是创建了一个状态的转移图,思想很 ...
- AC自动机(AC automation)
字典树+KMP 参考自: http://www.cppblog.com/mythit/archive/2009/04/21/80633.html ; //字典大小 //定义结点 struct node ...
- 转自kuangbin的AC自动机(赛前最后一博)
有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配. AC自动机 其实 就是创建了一个状态的转移图,思想很 ...
- HDU - 2222,HDU - 2896,HDU - 3065,ZOJ - 3430 AC自动机求文本串和模式串信息(模板题)
最近正在学AC自动机,按照惯例需要刷一套kuangbin的AC自动机专题巩固 在网上看过很多模板,感觉kuangbin大神的模板最为简洁,于是就选择了用kuangbin大神的模板. AC自动机其实就是 ...
- 「kuangbin带你飞」专题十七 AC自动机
layout: post title: 「kuangbin带你飞」专题十七 AC自动机 author: "luowentaoaa" catalog: true tags: - ku ...
- [专题总结]AC自动机
其实前面的模板也不是1A,我在题库里提前做过,也不必在意罚时,刚开始我在做别的专题 裸模板我就不说了,各个博客讲解的很明白 void insert(string s){ ,len=s.size(); ...
- [专题汇总]AC自动机
1.The 2011 ACM-ICPC Asia Dalian Regional Contest ZOJ 3545 Rescue the Rabbit 简单的AC自动机+状压DP, 状态DP[nod ...
随机推荐
- Linux常用命令和Shell编程基础
目录相关 cd - .与.. 分别表示当前目录和父目录 - ~与$HOME 都是指当前用户的主目录 - cd – 切换到上一次所在的目录(不一定是父目录) pwd - pwd 显示当前目录 - $PW ...
- WdatePicker 使用
限制范围为今年之后的3年 $("input[name='year']").attr("onClick","WdatePicker({dateFmt:' ...
- Windows无法完成安装,若要在此计算机上安装Windows,请中心启动安装。
现在安装系统已经很简单了,我觉得U盘启动的话两步就差不多了, 壹:设置BIOS,将U盘启动作为系统默认启动选项 贰:直接进去大白菜之类的,一键安装... 今天终于看到第三部了, 报错:Windows无 ...
- D3.js学习(三)
上一节中,我们已经画出了图表,并且给图表添加了坐标轴的标签和标题,在这一节中,我们将要学习几个绘制线条不同特性的几个函数,以及给图表添加格栅.ok,进入话题! 如何给线条设置绘制的样式? 这个其实非常 ...
- MyBatis入门案例
1.案例架构 2.引入jar 包 3.书写配置文件mybatis-config.xml <?xml version="1.0" encoding="UTF-8&qu ...
- laravel数据库迁移(三)
laravel号称世界上最好的框架,数据库迁移算上一个,在这里先简单入个门: laravel很强大,它把表中的操作写成了migrations迁移文件,然后可以直接通过迁移文件来操作表.所以 , 数据迁 ...
- linux 下Qt WebEngine 程序打包简单记录
本次记录仅作参考. 程序说明: 程序是一个编解码器控制管理的工具,使用到的库有:Qt的WebEngine.OpenGL模块.poco库.libmicrohttpd.libcurl.libvlc.同时程 ...
- 整理文件,翻出了以前作的ps稿 (^o^)c旦``
稍稍会那么一点PS,小意思
- QL Server 2008 所有账号丢失sysadmin权限,sa账号亦没有开启,该如何解决??
1. 用Run as a administrator打开命令提示符里输入NET STOP MSSQLSERVER, 即停止MSSQLSERVER运行. 2. 在命令提示符里输入 NET START M ...
- Python实现简单的Web完整版(一)
在拖了一周之后,今天终于在一个小时之内将一个迷你的Web写出来了,最近改其它项目的bug头好大,但是好喜欢这样的状态. 黑色的12月,所有的任务都聚集在了12月,然后期末考试也顾不上好好复习了,但是但 ...