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自动机专题的更多相关文章

  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. 阿里云推送SDK在某些机型(某米为主)下崩溃问题的解决方法

    引言 最近APP上线,遇到一个比较诡异的问题.最后竟然和dex文件有关,也是醉了,看来还得深入底层学习啊. 问题描述 在集成阿里推送SDK时,需要在Application中进行初始化,大多数Andro ...

  2. VS2015 调试Web项目 遭遇 HTTP 错误 500.23 - Internal Server Error

    此错误是因为项目使用的托管管道模式有问题,将集成改为传统即可 选中项目 进入项目属性 ,如图界面

  3. Mysql安装及主从复制配置

    1.下载 mysql数据库 wget http://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.9-linux-glibc2.5-x86_64.ta ...

  4. Objective-C中的浅拷贝和深拷贝(转载)

    本文转自:http://segmentfault.com/blog/channe/1190000000604331 浅拷贝 浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间.如: ...

  5. ORACLE LINUX 6.3 + ORACLE 11.2.0.3 RAC + VBOX安装文档

    ORACLE LINUX 6.3 + ORACLE 11.2.0.3 RAC + VBOX安装文档 2015-10-21 12:51 525人阅读 评论(0) 收藏 举报  分类: Oracle RA ...

  6. 163邮件出错:不允许使用邮箱名称。 服务器响应为: authentication is required,smtp7,C8CowEDpS0+Uke9VvSmXBg--.546S2 1441763733

    原因:用163邮箱发邮件,需开启smtp服务,开启服务时,要求使用客户端授权码. 在.net中,使用smtp发邮件,在验证中使用的密码,是上面所讲的客户端授权码,而不是注册和web登录时用的邮箱密码. ...

  7. 没有我的A协

    我离开A协(北京林业大学ACM爱好者协会)有段时间了,严格算来,应该有4年了.现在协会里的大部分人我都不认识.A协在我离开之后的这段时间里也产生了翻天覆地的变化. A协已经不只是一个以竞赛培训为目的的 ...

  8. appium V1.5.x变化

    使用 npm安装 appium之后,会发现已经进入1.5 [Appium] Welcome to Appium v1.5.0 [Appium] Appium REST http interface l ...

  9. (一)Netty源码学习笔记之概念解读

    尊重原创,转载注明出处,原文地址:http://www.cnblogs.com/cishengchongyan/p/6121065.html  博主最近在做网络相关的项目,因此有契机学习netty,先 ...

  10. java 深入技术七(类的结构和反射)

    1.java反射(reflect) java 类的结构 java反射机制就是把java类按结构分解,每一部分对应特定的反射类 java反射机制允许运行时加载,探知和使用在编译期间完全未知的classe ...