2018-03-15 10:25:02

在计算机科学中,Aho–Corasick算法是由Alfred V. Aho和Margaret J.Corasick 发明的字符串搜索算法,用于在输入的一串字符串中匹配有限组“字典”中的子串。它与普通字符串匹配的不同点在于同时与所有字典串进行匹配。算法均摊情况下具有近似于线性的时间复杂度,约为字符串的长度加所有匹配的数量。

AC自动机主要依靠构造一个有限状态机(类似于在一个trie树中添加失配指针)来实现。这些额外的失配指针允许在查找字符串失败时进行回退(例如设Trie树的单词cat匹配失败,但是在Trie树中存在另一个单词cart,失配指针就会指向前缀ca),转向某前缀的其他分支,免于重复匹配前缀,提高算法效率。

当一个字典串集合是已知的(例如一个计算机病毒库), 就可以以离线方式先将自动机求出并储存以供日后使用,在这种情况下,算法的时间复杂度为输入字符串长度和匹配数量之和。

UNIX系统中的一个命令fgrep就是以AC自动机算法作为基础实现的。

一、自动机

自动机是计算理论的一个概念,其实是一张“图”,每个点是一个“状态”,而边则是状态之间的转移,根据条件能指导从一个状态走向另一个状态。很多字符串匹配算法都是基于自动机模型的,比如被广泛使用的正则表达式。

二、AC自动机

AC自动机可以看成是对KMP算法的推广,KMP算法是一种单模字符串匹配算法,AC自动机是多模字符串匹配算法,可以一次对多个pattern进行匹配。

AC自动机的建立流程也很简单,主要分为以下几步:

1.建Trie树


2.在Trie树上建立失配指针,成为AC自动机


3.自动机上匹配字符串

下面以模式串he/ she/ his /hers为例,待检测文本为“ushers”。

1、建立Trie树

建立Trie树可以说是非常模板了。

    class TrieNode {
TrieNode[] children;
TrieNode fail;
boolean isWord; TrieNode() {
children = new TrieNode[26];
fail = null;
isWord = false;
}
} TrieNode root; void bulidTrie(String[] patterns) {
root = new TrieNode();
for (String pattern : patterns) {
TrieNode cur = root;
for (int i = 0; i < pattern.length(); i++) {
if (cur.children[pattern.charAt(i) - 'a'] == null)
cur.children[pattern.charAt(i) - 'a'] = new TrieNode();
cur = cur.children[pattern.charAt(i) - 'a'];
}
cur.isWord = true;
}
}

2、建立失配指针

AC自动机的核心就是建立失配指针,其思路和KMP算法非常类似,在KMP中如果文本的text[i...j] 和 pattern[0...j - i]在text[j]出失配,KMP采取的思路是计算pattern[0...j - i - 1]的最长公共前后缀,然后将pattern向后滑动数位,从最长公共前后缀之后继续比较,如果依然失配,则重复上述的流程,直到到首位,如果依然失配,则text下移。

在AC自动机中也是这样,构造失败指针的过程概括起来就一句话:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。具体操作起来只需要:先把root加入队列(root的失败指针指向自己或者NULL),这以后我们每处理一个点,就把它的所有儿子加入队列,队列为空。

    void core() {
Queue<TrieNode> queue = new LinkedList<TrieNode>();
queue.add(root);
while (!queue.isEmpty()) {
TrieNode cur = queue.poll();
for (int i = 0; i < 26; i++) {
if (cur.children[i] != null) {
if (cur == root) cur.children[i].fail = root;
else {
TrieNode tmp = cur.fail;
while (tmp != null) {
if (tmp.children[i] != null) {
cur.children[i].fail = tmp.children[i];
break;
}
tmp = tmp.fail;
}
if (tmp == null) cur.children[i].fail = root;
}
queue.add(cur.children[i]);
}
}
}
}

3、在自动机上进行匹配

    int query(String text) {
int res = 0;
TrieNode pre = root;
for (int i = 0; i < text.length(); i++) {
int index = text.charAt(i) - 'a';
while (pre.children[index] == null && pre != root) pre = pre.fail;
if (pre == root && pre.children[index] == null) continue;
pre = pre.children[index];
TrieNode tmp = pre;
while (tmp != root && tmp.isWord) {
res++;
tmp.isWord = false;
tmp = tmp.fail;
}
}
return res;
}

完整代码:

import java.util.LinkedList;
import java.util.Queue; public class AhoCorasick {
class TrieNode {
TrieNode[] children;
TrieNode fail;
boolean isWord; TrieNode() {
children = new TrieNode[26];
fail = null;
isWord = false;
}
} TrieNode root; AhoCorasick() {
root = new TrieNode();
} void bulidTrie(String[] patterns) {
for (String pattern : patterns) {
TrieNode cur = root;
for (int i = 0; i < pattern.length(); i++) {
if (cur.children[pattern.charAt(i) - 'a'] == null)
cur.children[pattern.charAt(i) - 'a'] = new TrieNode();
cur = cur.children[pattern.charAt(i) - 'a'];
}
cur.isWord = true;
}
} void core() {
Queue<TrieNode> queue = new LinkedList<TrieNode>();
queue.add(root);
while (!queue.isEmpty()) {
TrieNode cur = queue.poll();
for (int i = 0; i < 26; i++) {
if (cur.children[i] != null) {
if (cur == root) cur.children[i].fail = root;
else {
TrieNode tmp = cur.fail;
while (tmp != null) {
if (tmp.children[i] != null) {
cur.children[i].fail = tmp.children[i];
break;
}
tmp = tmp.fail;
}
if (tmp == null) cur.children[i].fail = root;
}
queue.add(cur.children[i]);
}
}
}
} int query(String text) {
int res = 0;
TrieNode pre = root;
for (int i = 0; i < text.length(); i++) {
int index = text.charAt(i) - 'a';
while (pre.children[index] == null && pre != root) pre = pre.fail;
if (pre == root && pre.children[index] == null) continue;
pre = pre.children[index];
TrieNode tmp = pre;
while (tmp != root && tmp.isWord) {
res++;
tmp.isWord = false;
tmp = tmp.fail;
}
}
return res;
} public static void main(String[] args) {
AhoCorasick ac = new AhoCorasick();
String[] patterns = new String[]{"he", "she", "his", "hers"};
ac.bulidTrie(patterns);
ac.core();
int ans = ac.query("ushers");
System.out.println(ans);
}
}

Aho-Corasick算法的更多相关文章

  1. 多模字符串匹配算法-Aho–Corasick

    背景 在做实际工作中,最简单也最常用的一种自然语言处理方法就是关键词匹配,例如我们要对n条文本进行过滤,那本身是一个过滤词表的,通常进行过滤的代码如下 for (String document : d ...

  2. Aho - Corasick string matching algorithm

    Aho - Corasick string matching algorithm 俗称:多模式匹配算法,它是对 Knuth - Morris - pratt algorithm (单模式匹配算法) 形 ...

  3. Flashtext:大规模数据清洗的利器

    Flashtext:大规模数据清洗的利器 在这篇文章中,我们将介绍一种新的关键字搜索和替换的算法:Flashtext 算法.Flashtext 算法是一个高效的字符搜索和替换算法.该算法的时间复杂度不 ...

  4. vivo 敏感词匹配系统的设计与实践

    一.前言 谛听系统是vivo的内容审核平台,保障了vivo各互联网产品持续健康的发展.谛听支持审核多种内容类型,但日常主要审核的内容是文本,下图是一个完整的文本审核流程,包括名单匹配.敏感词匹配.AI ...

  5. 算法 - DNA搜索 - Ako Corasick

    场景:从很长的字符串(输入字符串.DNA)中搜索大量固定字符串(字典.基因) 题目:Determining DNA Health | HackerRank 算法:Aho–Corasick algori ...

  6. Aho-Corasick算法、多模正则匹配、Snort入门学习

    希望解决的问题 . 在一些高流量.高IO的WAF中,是如何对规则库(POST.GET)中的字符串进行多正则匹配的,是单条轮询执行,还是多模式并发执行 . Snort是怎么组织.匹配高达上千条的正则规则 ...

  7. 敏感词过滤的算法原理之 Aho-Corasick 算法

    参考文档 http://www.hankcs.com/program/algorithm/implementation-and-analysis-of-aho-corasick-algorithm-i ...

  8. AC 自动机

    AC自动机(Aho-Corasick Automata)是经典的多模式匹配算法.从前我学过这个算法,但理解的不深刻,现在已经十分不明了了.现在发觉自己对大部分算法的掌握都有问题,决定重写一系列博客把学 ...

  9. 中文分词系列(二) 基于双数组Tire树的AC自动机

    秉着能偷懒就偷懒的精神,关于AC自动机本来不想看的,但是HanLp的源码中用户自定义词典的识别是用的AC自动机实现的.唉-没办法,还是看看吧 AC自动机理论 Aho Corasick自动机,简称AC自 ...

随机推荐

  1. linix防火墙设置之顺序设置问题 -- 解决防火墙规则顺序和插入规则到指定序号的问题

    转载于百度经验:https://jingyan.baidu.com/article/ae97a646ce58c2bbfd461d90.html 无论是硬件防火墙还是软件防火墙都会有一个规则序列的问题, ...

  2. 一键搞定JavaEE应用,JRE+Tomcat+Mysql-JaveEE绿色运行环境JTM0.9版 (转载)

    这是一个集成了jre,tomcat,mysql的绿色运行环境, 直接解压之后就可以运行.不需要用户自己安装jre,tomcat,mysql,一键到位,看起来像是桌面式的web应用.换句话说,就像是we ...

  3. docker swarm+register-web+shipyard搭建

    1.swarm安装 swarm安装有很多种服务注册的方式,token.etcd.zookeeper,本文主要以swarm默认的token方式进行安装.因为最新的docker已经集成了swarm,所以从 ...

  4. codeforces#510 Div2

    pre过了三题 后来A题被hack了 B题终测挂了 两题其实都是有一个小细节没有处理好 中间C还因为cinT了一次 唉本来打的还不错的 还是太菜了 继续加油吧 A-Benches 有n张椅子 原来第i ...

  5. ar的主流算法

    基于无标志AR:代表作是PTAM/M,Mixare,将是AR未来的发展方向 跟踪技术可以大致分成两大类,一类是基于特征的跟踪(Feature Based Tracking),比如通过跟踪从输入图像中抽 ...

  6. Meaning of “const” last in a C++ method declaration?

    函数尾部的const是什么意思? 1 Answer by Jnick Bernnet A "const function", denoted with the keyword co ...

  7. hihoCoder_1449_后缀自动机三·重复旋律6

    #1449 : 后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数 ...

  8. uboot ping doesn’t work

    Ping doesn't work Ping from U-Boot to a host should work. Ping from a host to U-Boot should not. U-B ...

  9. MySQL的知识海洋

    第一篇:初识数据库 第二篇:库操作 第三篇:表操作 第四篇:数据操作 第五篇:视图.触发器.存储过程.函数.事物与数据库锁 第六篇:索引原理与慢查询优化 第七篇:pymysql(用python连接以及 ...

  10. 8.Git撤销修改

    有一个文件内容如下: $ cat README.md the first ... the second ... the third ... - 文件自修改后还没有被放到暂存区,现在,撤销修改就回到和版 ...