Trie树:

把若干个单词按前缀合并就得到一棵树,这棵树称为Trie树。Trie树是有根树,每条边表示一个字符,每个节点表示一个从根到当前节点的唯一路径上的字符依次连接得到的字符串。由于空串是任何串的前缀,因此根就表示“空串”这个串。如何区分单词节点和非单词节点呢?插入单词的时候对每个节点mark一下即可。

KMP算法思想:

能匹配就匹配,不能匹配就进行尽量小的平移来达到匹配。

有限自动机:

自动机是一个处理信息的机器,它的核心是状态和状态转移(和dp一样??),通过设计不同的状态和状态转移函数,来得到不同功能的自动机,因此自动机的应用非常广泛。

ac自动机

对字符串S构造一个这样的自动机:假设自动机扫描字符串T后处于状态w(w是一个整数,表示匹配长度),那么T的后w个字符是S的前缀,且w是满足这个性质的最大值。那么状态转移函数就可以这样定义:w + 字符c --> q,表示[Tc]的后q个字符是S的前缀,且这个q是满足这个性质的最大值。因此,状态转移矩阵很容易在O(m3Σ)的时间内求出来。

上述自动机慢在确定q需要花费O(m2)的时间,由kmp算法思想知道,如果S[w] == c,那么q = w + 1,否则w需要回退。那么我们利用kmp的回退数组(next数组),可以将复杂度降低到接近O(mΣ)。

同样,考虑多串的情形,则利用队列分层计算失配数组。这里会产生1个新的问题,假设当前匹配到了某个状态,这个状态表示的字符串为S,那么意味着不仅找到了S,而且找到了S的所有后缀,具体解决方法是给每个状态增加1个后缀链接,指向它的最大后缀单词,这样在找的时候要加速不少。

code(hdu2222,统计有多少模板串出现在了文本串里面):

#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define pb(x) push_back(x)
#define mp(x, y) make_pair(x, y)
#define all(a) (a).begin(), (a).end()
#define mset(a, x) memset(a, x, sizeof(a))
#define mcpy(a, b) memcpy(a, b, sizeof(b))
#define cas() int T, cas = 0; cin >> T; while (T --)
template<typename T>bool umax(T&a, const T&b){return a<b?(a=b,true):false;}
template<typename T>bool umin(T&a, const T&b){return b<a?(a=b,true):false;}
typedef long long ll;
typedef pair<int, int> pii;
#ifndef ONLINE_JUDGE
#include "local.h"
#endif int ans; class ACAutomaton {
public:
void clear() {
memset(node, 0, sizeof(node));
sz = 1;
}
void insert(char P[]) {
int now = 0;
for (int i = 0; P[i]; i ++) {
int id = index(P[i]);
if (!node[now][id]) node[now][id] = sz ++;
now = node[now][id];
}
node[now].cnt ++;
node[now].final_state = true;
}
void build() {
queue<int> Q;
for (int i = 0; i < SZ; i ++) {
if (node[0][i]) {
Q.push(node[0][i]);
}
}
while (!Q.empty()) {
int ch = Q.front(); Q.pop();
for (int i = 0; i < SZ; i ++) {
int next = node[ch][i];
if (next) {
int now = node[ch].fail;
while (now && !node[now][i]) now = node[now].fail;
int buf = node[now][i];
node[next].last = node[next].fail = buf;
if (!node[buf].final_state) node[next].last = node[buf].last;
Q.push(next);
}
}
}
}
void work(char T[]) {
int now = 0;
for (int i = 0; T[i]; i ++) {
int id = index(T[i]);
while (now && !node[now][id]) now = node[now].fail;
now = node[now][id];
find(i, now);
}
}
private:
const static int N = 250007;
const static int SZ = 26;
struct Node {
int next[SZ], fail, last;
bool final_state;
int cnt;
int &operator[] (int p) { return next[p]; }
};
Node node[N];
int sz;
void find(int p, int now) {
if (now == 0) return;
if (node[now].final_state) process(p, now);
find(p, node[now].last);
}
int index(char ch) {
return ch - 'a';
}
void process(int p, int now) {
ans += node[now].cnt;
node[now].final_state = false;
}
};
ACAutomaton ac;
char s[100], t[1234567]; int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
int T, n;
cin >> T;
while (T --) {
cin >> n;
ac.clear();
for (int i = 0; i < n; i ++) {
scanf("%s", s);
ac.insert(s);
}
scanf("%s", t);
ac.build();
ans = 0;
ac.work(t);
cout << ans << endl;
}
return 0;
}

  

动手实现--AC自动机的更多相关文章

  1. AC自动机:BZOJ 2434 阿狸的打字机

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1834  Solved: 1053[Submit][Sta ...

  2. BZOJ 3881 [Coci2015]Divljak(AC自动机+树状数组)

    建立AC自动机然后,加入一个串之后考虑这个串的贡献.我们把这个串扔到AC自动机里面跑.最后对经过每一个点到的这个点在fail树的根的路径上的点有1的贡献.求链的并,我们把这些点按DFS序排序,然后把每 ...

  3. 基于trie树做一个ac自动机

    基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...

  4. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

  5. python爬虫学习(11) —— 也写个AC自动机

    0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...

  6. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  7. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  8. BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]

    1212: [HNOI2004]L语言 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1367  Solved: 598[Submit][Status ...

  9. [AC自动机]【学习笔记】

    Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...

随机推荐

  1. Joomla 3.4.6 RCE 分析

    Joomla 3.4.6 RCE 漏洞分析,首发先知社区: https://xz.aliyun.com/t/6522 漏洞环境及利用 Joomla 3.4.6 : https://downloads. ...

  2. [Laravel] 自带分页实现以及links方法不存在错误

    自带分页实现其实挺简单的,但是我在实现的时候报错!找了很久才找出原因! 废话不说上码 控制器LeeController.php层 <?php namespace App\Http\control ...

  3. response没有实现跳转,而是提示浏览器下载文件

    问题简述: web项目中,response没能实现重定向跳转网页,而是通知浏览器下载文件. 代码如下: response.getWriter().write("<h1 style='c ...

  4. shiro:加密及密码比对器(三)

    基于[自定义remle(二)]项目增加加密功能 1:数据库t_user表增加一列(盐) 增加字段:salt CREATE TABLE `t_user` ( `id` int(11) NOT NULL ...

  5. SpringMVC视图解析中的 forward: 与 redirect: 前缀

    在 SpringMVC 中,可以指定画面的跳转方式.使用 forward: 前缀实现请求转发跳转,使用 redirect: 前缀实现重定向跳转.有前缀的转发和重定向操作和配置的视图解析器没有关系,视图 ...

  6. TensorFlow keras vgg16net的使用

    from tensorflow.python.keras.applications.vgg16 import VGG16,preprocess_input,decode_predictions fro ...

  7. 第八次-非确定的自动机NFA确定化为DFA

     提交作业 NFA 确定化为 DFA 子集法: f(q,a)={q1,q2,…,qn},状态集的子集 将{q1,q2,…,qn}看做一个状态A,去记录NFA读入输入符号之后可能达到的所有状态的集合. ...

  8. [SVN] Couldn't perform atomic initialization

    svn: Commit failed (details follow): svn: Couldn't perform atomic initialization It was because the ...

  9. Fedora 21下lingo14配置

    Install lingo14 during Fedora 21 The first step:Download lingo14Download address:http://www.lindo.co ...

  10. 【三剑客】awk运算符

    1. 算术运算符:+,-,*,/,% [root@oldboy test]# awk 'BEGIN{a=50;b=20;print "(a+b)=",(a+b)}' (a+b)= ...