动手实现--AC自动机
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自动机的更多相关文章
- AC自动机:BZOJ 2434 阿狸的打字机
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1834 Solved: 1053[Submit][Sta ...
- BZOJ 3881 [Coci2015]Divljak(AC自动机+树状数组)
建立AC自动机然后,加入一个串之后考虑这个串的贡献.我们把这个串扔到AC自动机里面跑.最后对经过每一个点到的这个点在fail树的根的路径上的点有1的贡献.求链的并,我们把这些点按DFS序排序,然后把每 ...
- 基于trie树做一个ac自动机
基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
- python爬虫学习(11) —— 也写个AC自动机
0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3198 Solved: 1532[Submit][Status ...
- BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]
1212: [HNOI2004]L语言 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1367 Solved: 598[Submit][Status ...
- [AC自动机]【学习笔记】
Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...
随机推荐
- JS 的基础概念
本篇文章主要讲述js的基础知识! 首先,我们要明白什么是JS,JS就是 javascript 的简称,是一种轻量级,弱类型的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能, ...
- CVE-2019-1388:Windows UAC 本地提权复现
0x01 简介 用户帐户控制(User Account Control,简写作UAC)是微软公司在其Windows Vista及更高版本操作系统中采用的一种控制机制.其原理是通知用户是否对应用程序使用 ...
- linq详细案例
LINQ to SQL语句(1)之Where 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句.Where操 ...
- tensorflow1.0 构建lstm做图片分类
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #this is data mni ...
- liunx常用知识基本命令大全
liunx基础命令使用 标签(空格分隔):liunx常用命令 网络配置 虚拟网卡的绝对路径 /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 ...
- php +go关键字实现协程
来源: https://studygolang.com/articles/17631?fr=sidebar 今天在知乎浏览时忽然发现了一个有趣的东西,php竟然可以实现协程的实现,而且还是通过go关键 ...
- 2019-2020-1 20199310《Linux内核原理与分析》第十一周作业
1.问题描述 在一个capability系统中,当一个程序运行时,对应的线程会初始化一系列capabilities(令牌).当线程尝试访问某个对象时,操作系统会检查该线程的capabilities,并 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第十二周作业
<Linux内核原理与分析>第十二周作业 一.本周内容概述: 通过编程理解 Set-UID 的运行机制与安全问题 完成实验楼上的<SET-UID程序漏洞实验> 二.本周学习内容 ...
- [Qt] 数字转换为 QString
数字转换为 QString 静态函数 number(), asprintf() 公用函数 setNum(), sprintf() QString::number(), setNum() 可转换进制
- .Net平台GC VS JVM垃圾回收
前言 不知道你平时是否关注程序内存使用情况,我是关注的比较少,正好借着优化本地一个程序的空对比了一下.Net平台垃圾回收和jvm垃圾回收,顺便用dotMemory看了程序运行后的内存快照,生成内存快照 ...