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.

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <queue>
  6. #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
  7. #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
  8. using namespace std;
  9.  
  10. const int MAX_N = ( + );
  11. struct Trie {
  12. int next[MAX_N][], End[MAX_N], fail[MAX_N];
  13. int root, L;
  14. int NewNode()
  15. {
  16. FOR(i, , ) next[L][i] = -;
  17. End[L++] = ;
  18. return L - ;
  19. }
  20. void Init()
  21. {
  22. L = ;
  23. root = NewNode();
  24. }
  25. void Insert(char *str)
  26. {
  27. int len = strlen(str), now = root;
  28. FOR(i, , len) {
  29. int id = str[i] - 'a';
  30. if (next[now][id] == -) next[now][id] = NewNode();
  31. now = next[now][id];
  32. }
  33. ++End[now];
  34. }
  35. void Build()
  36. {
  37. queue<int > que;
  38. fail[root] = root;
  39. FOR(i, , ) {
  40. if (next[root][i] == -) next[root][i] = root;
  41. else {
  42. fail[next[root][i]] = root;
  43. que.push(next[root][i]);
  44. }
  45. }
  46. while (!que.empty()) {
  47. int now = que.front();
  48. que.pop();
  49. FOR(i, , ) {
  50. if (next[now][i] == -) {
  51. next[now][i] = next[fail[now]][i];
  52. } else {
  53. fail[next[now][i]] = next[fail[now]][i];
  54. que.push(next[now][i]);
  55. }
  56. }
  57. }
  58. }
  59. int Query(char *str)
  60. {
  61. int len = strlen(str), now = root, res = ;
  62. FOR(i, , len) {
  63. int id = str[i] - 'a';
  64. now = next[now][id];
  65. int tmp = now;
  66. while (tmp != root) {
  67. res += End[tmp];
  68. End[tmp] = ;
  69. tmp = fail[tmp];
  70. }
  71. }
  72. return res;
  73. }
  74. } AC;
  75.  
  76. int n;
  77. char str[ + ];
  78.  
  79. int main()
  80. {
  81. int Cas;
  82. scanf("%d", &Cas);
  83. while (Cas--) {
  84. AC.Init();
  85. scanf("%d", &n);
  86. REP(i, , n) {
  87. scanf("%s", str);
  88. AC.Insert(str);
  89. }
  90. AC.Build();
  91. scanf("%s", str);
  92. printf("%d\n", AC.Query(str));
  93. }
  94. return ;
  95. }

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896

思路:和上题差不多,只是用End数组来记录序号而已。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <queue>
  6. #include <vector>
  7. #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
  8. #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
  9. using namespace std;
  10.  
  11. const int MAX_N = ( + );
  12. struct Trie {
  13.  
  14. int next[MAX_N][], End[MAX_N], fail[MAX_N];
  15. int root, L;
  16. int NewNode() {
  17. FOR(i, , ) next[L][i] = -;
  18. End[L++] = ;
  19. return L - ;
  20. }
  21. void Init() {
  22. L = ;
  23. root = NewNode();
  24. }
  25.  
  26. void Insert(char *str, int index) {
  27. int len = strlen(str), now = root;
  28. FOR(i, , len) {
  29. int id = str[i];
  30. if (next[now][id] == -) next[now][id] = NewNode();
  31. now = next[now][id];
  32. }
  33. End[now] = index;
  34. }
  35. void Build() {
  36. queue<int > que;
  37. fail[root] = root;
  38. FOR(i, , ) {
  39. if (next[root][i] == -) next[root][i] = root;
  40. else {
  41. fail[next[root][i]] = root;
  42. que.push(next[root][i]);
  43. }
  44. }
  45. while (!que.empty()) {
  46. int now = que.front();
  47. que.pop();
  48. FOR(i, , ) {
  49. if (next[now][i] == -) {
  50. next[now][i] = next[fail[now]][i];
  51. } else {
  52. fail[next[now][i]] = next[fail[now]][i];
  53. que.push(next[now][i]);
  54. }
  55. }
  56. }
  57. }
  58. void Query(char *str, vector<int > &ans) {
  59. int len = strlen(str), now = root;
  60. FOR(i, , len) {
  61. now = next[now][str[i]];
  62. int tmp = now;
  63. while (tmp != root) {
  64. if (End[tmp]) ans.push_back(End[tmp]);
  65. tmp = fail[tmp];
  66. }
  67. }
  68. }
  69.  
  70. } AC;
  71.  
  72. int N, M, res;
  73. char str[ + ];
  74. vector<int > ans[ + ];
  75.  
  76. int main()
  77. {
  78. AC.Init();
  79. scanf("%d", &N);
  80. REP(i, , N) {
  81. scanf("%s", str);
  82. AC.Insert(str, i);
  83. }
  84. AC.Build();
  85. scanf("%d", &M);
  86. FOR(i, , M) {
  87. scanf("%s", str);
  88. AC.Query(str, ans[i]);
  89. }
  90. res = ;
  91. FOR(i, , M) {
  92. if ((int)ans[i].size()) {
  93. printf("web %d:", i + );
  94. sort(ans[i].begin(), ans[i].end());
  95. FOR(j, , (int)ans[i].size()) printf(" %d", ans[i][j]);
  96. puts("");
  97. ++res;
  98. }
  99. }
  100. printf("total: %d\n", res);
  101. return ;
  102. }

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3065

思路:用一个数组来记录模式串在主串中出现的次数。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <queue>
  6. #define FOR(i, a, b) for (int i = (a); i < (b); ++i)
  7. #define REP(i, a, b) for (int i = (a); i <= (b); ++i)
  8. using namespace std;
  9.  
  10. const int MAX_N = ( + );
  11.  
  12. int N, num[ + ];
  13. char ss[ + ][];
  14. char str[ + ];
  15.  
  16. struct Trie {
  17. int next[MAX_N][], End[MAX_N], fail[MAX_N];
  18. int root, L;
  19. int NewNode() {
  20. FOR(i, , ) next[L][i] = -;
  21. End[L++] = -;
  22. return L - ;
  23. }
  24.  
  25. void Init() {
  26. L = ;
  27. root = NewNode();
  28. }
  29.  
  30. void Insert(char *str, int index) {
  31. int len = strlen(str), now = root;
  32. FOR(i, , len) {
  33. if (next[now][str[i]] == -) next[now][str[i]] = NewNode();
  34. now = next[now][str[i]];
  35. }
  36. End[now] = index;
  37. }
  38.  
  39. void Build() {
  40. queue<int > que;
  41. fail[root] = root;
  42. FOR(i, , ) {
  43. if (next[root][i] == -) next[root][i] = root;
  44. else {
  45. fail[next[root][i]] = root;
  46. que.push(next[root][i]);
  47. }
  48. }
  49. while (!que.empty()) {
  50. int now = que.front();
  51. que.pop();
  52. FOR(i, , ) {
  53. if (next[now][i] == -) next[now][i] = next[fail[now]][i];
  54. else {
  55. fail[next[now][i]] = next[fail[now]][i];
  56. que.push(next[now][i]);
  57. }
  58. }
  59. }
  60. }
  61.  
  62. void Query(char *str) {
  63. memset(num, , sizeof(num));
  64. int len = strlen(str), now = root;
  65. FOR(i, , len) {
  66. now = next[now][str[i]];
  67. int tmp = now;
  68. while (tmp != root) {
  69. if (End[tmp] != -) ++num[End[tmp]];
  70. tmp = fail[tmp];
  71. }
  72. }
  73. FOR(i, , N) {
  74. if (num[i]) printf("%s: %d\n", ss[i], num[i]);
  75. }
  76. }
  77.  
  78. } AC;
  79.  
  80. int main()
  81. {
  82. while (~scanf("%d", &N)) {
  83. AC.Init();
  84. scanf("%d", &N);
  85. FOR(i, , N) {
  86. scanf("%s", ss[i]);
  87. AC.Insert(ss[i], i);
  88. }
  89. AC.Build();
  90. scanf("%s", str);
  91. AC.Query(str);
  92. }
  93. return ;
  94. }

题目链接:http://poj.org/problem?id=2778

思路:需要用到的知识:有向图中点A到点B走K步的路径数等于有向图原始矩阵的K次幂。然后对于已经建好的Trie图,我们就可以建图了,如果某个节点A不是终止节点并且这个节点的next节点B也不是终止节点,那么就连边(表示从A点走1步到节点B的方法有1种)。建好图之后就是矩阵的快速幂了,然后在统计节点0(根节点)到其余节点走N步的方法数的总和。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <queue>
  6. #define REP(i, a, b) for (int i = (a); i < (b); ++i)
  7. #define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
  8. using namespace std;
  9.  
  10. const int MAX_N = ( + );
  11. const int MOD = ();
  12. int M, N;
  13. char str[];
  14.  
  15. struct Matrix {
  16. long long mat[MAX_N][MAX_N];
  17. int n;
  18. Matrix() {}
  19. Matrix(int _n)
  20. {
  21. n = _n;
  22. REP(i, , n)
  23. REP(j, , n) mat[i][j] = ;
  24. }
  25. Matrix operator *(const Matrix &b) const
  26. {
  27. Matrix c = Matrix(n);
  28. REP(i, , n) {
  29. REP(j, , n) {
  30. REP(k, , n) {
  31. c.mat[i][j] += mat[i][k] * b.mat[k][j];
  32. if (c.mat[i][j] >= MOD) c.mat[i][j] %= MOD;
  33. }
  34. }
  35. }
  36. return c;
  37. }
  38.  
  39. };
  40.  
  41. Matrix Pow(Matrix mat, int n)
  42. {
  43. Matrix ONE = Matrix(mat.n);
  44. REP(i, , mat.n) ONE.mat[i][i] = ;
  45. Matrix tmp = mat;
  46. while (n) {
  47. if (n & ) ONE = ONE * tmp;
  48. n >>= ;
  49. tmp = tmp * tmp;
  50. }
  51. return ONE;
  52. }
  53.  
  54. struct Trie {
  55. int next[MAX_N][], End[MAX_N], fail[MAX_N];
  56. int L, root;
  57. int NewNode()
  58. {
  59. REP(i, , ) next[L][i] = -;
  60. End[L++] = ;
  61. return L - ;
  62. }
  63.  
  64. void Init()
  65. {
  66. L = ;
  67. root = NewNode();
  68. }
  69.  
  70. int getID(char ch)
  71. {
  72. if (ch == 'A') return ;
  73. if (ch == 'C') return ;
  74. if (ch == 'G') return ;
  75. if (ch == 'T') return ;
  76. }
  77.  
  78. void Insert(char *str)
  79. {
  80. int len = strlen(str), now = root;
  81. REP(i, , len) {
  82. int id = getID(str[i]);
  83. if (next[now][id] == -) next[now][id] = NewNode();
  84. now = next[now][id];
  85. }
  86. End[now] = ;
  87. }
  88.  
  89. void Build()
  90. {
  91. queue<int > que;
  92. fail[root] = root;
  93. REP(i ,, ) {
  94. if (next[root][i] == -) next[root][i] = root;
  95. else {
  96. fail[next[root][i]] = root;
  97. que.push(next[root][i]);
  98. }
  99. }
  100. while (!que.empty()) {
  101. int now = que.front();
  102. que.pop();
  103. if (End[fail[now]]) End[now] = ;
  104. REP(i, , ) {
  105. if (next[now][i] == -) next[now][i] = next[fail[now]][i];
  106. else {
  107. fail[next[now][i]] = next[fail[now]][i];
  108. que.push(next[now][i]);
  109. }
  110. }
  111. }
  112. }
  113.  
  114. Matrix getMatrix()
  115. {
  116. Matrix res = Matrix(L);
  117. REP(i, , L)
  118. REP(j, , ) if (!End[next[i][j]]) ++res.mat[i][next[i][j]];
  119. return res;
  120. }
  121.  
  122. } AC;
  123.  
  124. int main()
  125. {
  126. while (~scanf("%d %d", &M, &N)) {
  127. AC.Init();
  128. FOR(i, , M) scanf("%s", str), AC.Insert(str);
  129. AC.Build();
  130. Matrix tmp = AC.getMatrix();
  131. tmp = Pow(tmp, N);
  132. long long ans = ;
  133. REP(i, , tmp.n) {
  134. ans += tmp.mat[][i];
  135. if (ans >= MOD) ans %= MOD;
  136. }
  137. printf("%lld\n", ans);
  138. }
  139. return ;
  140. }

AC自动机专题的更多相关文章

  1. AC自动机专题总结

    最近学习了AC自动机,做了notonlysuccess大牛里面的题,也该来个总结了. AC自动机(Aho-Corasick Automaton)在1975年产生于贝尔实验室,是著名的多模匹配算法之一. ...

  2. AC自动机 专题

    // 求目标串中出现了几个模式串 //==================== #include <stdio.h> #include <algorithm> #include ...

  3. 【原创】AC自动机小结

    有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配.           AC自动机 其实 就是创建了一个状态的转移图,思想很 ...

  4. AC自动机(AC automation)

    字典树+KMP 参考自: http://www.cppblog.com/mythit/archive/2009/04/21/80633.html ; //字典大小 //定义结点 struct node ...

  5. 转自kuangbin的AC自动机(赛前最后一博)

    有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配.           AC自动机 其实 就是创建了一个状态的转移图,思想很 ...

  6. HDU - 2222,HDU - 2896,HDU - 3065,ZOJ - 3430 AC自动机求文本串和模式串信息(模板题)

    最近正在学AC自动机,按照惯例需要刷一套kuangbin的AC自动机专题巩固 在网上看过很多模板,感觉kuangbin大神的模板最为简洁,于是就选择了用kuangbin大神的模板. AC自动机其实就是 ...

  7. 「kuangbin带你飞」专题十七 AC自动机

    layout: post title: 「kuangbin带你飞」专题十七 AC自动机 author: "luowentaoaa" catalog: true tags: - ku ...

  8. [专题总结]AC自动机

    其实前面的模板也不是1A,我在题库里提前做过,也不必在意罚时,刚开始我在做别的专题 裸模板我就不说了,各个博客讲解的很明白 void insert(string s){ ,len=s.size(); ...

  9. [专题汇总]AC自动机

    1.The 2011 ACM-ICPC Asia Dalian Regional Contest ZOJ 3545 Rescue the Rabbit  简单的AC自动机+状压DP, 状态DP[nod ...

随机推荐

  1. Linux常用命令和Shell编程基础

    目录相关 cd - .与.. 分别表示当前目录和父目录 - ~与$HOME 都是指当前用户的主目录 - cd – 切换到上一次所在的目录(不一定是父目录) pwd - pwd 显示当前目录 - $PW ...

  2. WdatePicker 使用

    限制范围为今年之后的3年 $("input[name='year']").attr("onClick","WdatePicker({dateFmt:' ...

  3. Windows无法完成安装,若要在此计算机上安装Windows,请中心启动安装。

    现在安装系统已经很简单了,我觉得U盘启动的话两步就差不多了, 壹:设置BIOS,将U盘启动作为系统默认启动选项 贰:直接进去大白菜之类的,一键安装... 今天终于看到第三部了, 报错:Windows无 ...

  4. D3.js学习(三)

    上一节中,我们已经画出了图表,并且给图表添加了坐标轴的标签和标题,在这一节中,我们将要学习几个绘制线条不同特性的几个函数,以及给图表添加格栅.ok,进入话题! 如何给线条设置绘制的样式? 这个其实非常 ...

  5. MyBatis入门案例

    1.案例架构 2.引入jar 包 3.书写配置文件mybatis-config.xml <?xml version="1.0" encoding="UTF-8&qu ...

  6. laravel数据库迁移(三)

    laravel号称世界上最好的框架,数据库迁移算上一个,在这里先简单入个门: laravel很强大,它把表中的操作写成了migrations迁移文件,然后可以直接通过迁移文件来操作表.所以 , 数据迁 ...

  7. linux 下Qt WebEngine 程序打包简单记录

    本次记录仅作参考. 程序说明: 程序是一个编解码器控制管理的工具,使用到的库有:Qt的WebEngine.OpenGL模块.poco库.libmicrohttpd.libcurl.libvlc.同时程 ...

  8. 整理文件,翻出了以前作的ps稿 (^o^)c旦``

    稍稍会那么一点PS,小意思   

  9. QL Server 2008 所有账号丢失sysadmin权限,sa账号亦没有开启,该如何解决??

    1. 用Run as a administrator打开命令提示符里输入NET STOP MSSQLSERVER, 即停止MSSQLSERVER运行. 2. 在命令提示符里输入 NET START M ...

  10. Python实现简单的Web完整版(一)

    在拖了一周之后,今天终于在一个小时之内将一个迷你的Web写出来了,最近改其它项目的bug头好大,但是好喜欢这样的状态. 黑色的12月,所有的任务都聚集在了12月,然后期末考试也顾不上好好复习了,但是但 ...