HanLP — Aho-Corasick DoubleArrayTire 算法 ACDAT - 基于双数组字典树的AC自动机

简介: HanLP — Aho-Corasick DoubleArrayTire 算法 ACDAT - 基于双数组字典树的AC自动机

双数组字典树能在O(1)(1是模式串长度)时间内高速完成单串匹配,并且内存消耗可控,然而软肋在于多模式匹配。如果要匹配多个模式串,必须先实现前缀查询,然后频繁截取文本后缀才可多匹配。比如 ushers、shers、hers…这样一份文本要回退扫描多遍,性能较低。既然 AC 自动机的goto表本身就是一棵字典树,能否利用双数组字典树来实现它呢?如果能用双数组字典树表达 AC自动机,就能集合两者的优点,得到一种近乎完美的数据结构。

ACDAT的基本原理是替换 AC自动机 的goto表,也可看作为一棵双数组字典树的每个状态(下标)附上额外的信息。上节提到, AC自动机 的goto表就是字典树,只不过 AC自动机 比字典树多了output 表和fail表。那么ACDAT的构建原理就是为每个状态(base[i]和check[i])构建output[i][]和fail[i]。具体说来,分为3步。

  • 构建trie树,让终止节点记住对应模式串的字典序。

即将所有模式串构建为一颗字典树,同时将终止状态绑定外部value。在实现上可以先用TreeMap简单实现。

  • 构建双数组字典树,在将每个状态映射到双数组时,让它记住自己在双数组中的下标。

与单独构建双数组Trie树不同,在为一个trie树State创建base[i]的时候,让该State记住自己的i,这样就建立State和下标的映射。

  • 构建AC自动机,此时fail表中存储的就是状态的下标。

在构建AC自动机时,每构建一个节点State的fail表,就利用上述映射下标State.id将fail[id]设为failState.id。对于output表,也是同理。

返回所有匹配到的模式串

/**
 * 匹配母文本
 *
 * @param text 一些文本
 * @return 一个pair列表
 */
public List<Hit<V>> parseText(String text)

其中Hit是一个表示命中结果的结构:

/**
 * 一个命中结果
 *
 * @param <V>
 */
public class Hit<V>
{
    /**
     * 模式串在母文本中的起始位置
     */
    public final int begin;
    /**
     * 模式串在母文本中的终止位置
     */
    public final int end;
    /**
     * 模式串对应的值
     */
    public final V value;
}

即时处理

AhoCorasickDoubleArrayTrie提供即时处理的结构:

/**
 * 处理文本
 *
 * @param text      文本
 * @param processor 处理器
 */
public void parseText(String text, IHit<V> processor)

其中IHit<V>是一个轻便的接口:

/**
 * 命中一个模式串的处理方法
 */
public interface IHit<V>
{
    /**
     * 命中一个模式串
     *
     * @param begin 模式串在母文本中的起始位置
     * @param end   模式串在母文本中的终止位置
     * @param value 模式串对应的值
     */
    void hit(int begin, int end, V value);
}

调用方法

import com.hankcs.hanlp.collection.AhoCorasick.AhoCorasickDoubleArrayTrie;
import com.hankcs.hanlp.dictionary.CoreDictionary;
import com.hankcs.hanlp.utility.LexiconUtility;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
public static void main(String[] args) throws IOException {
TreeMap<String, String> map = new TreeMap<>();
    String[] keyArray = new String[]
        {
            "清华",
            "清华大学",
            "清新",
            "中华",
            "华人"
        };
    for (String key : keyArray) {
        map.put(key, key);
    }
    AhoCorasickDoubleArrayTrie<String> act = new AhoCorasickDoubleArrayTrie<>();
    act.build(map);
    act.parseText("清华大学生都是华人", new AhoCorasickDoubleArrayTrie.IHit<String>() {
        @Override
        public void hit(int begin, int end, String value) {
            System.out.printf("[%d:%d]=%s\n", begin, end, value);
        }
    });
}

输出:

[0:2]=清华
[0:4]=清华大学
[7:9]=华人

单独的AhoCorasickDoubleArrayTrie类库:https://github.com/hankcs/AhoCorasickDoubleArrayTrie

目录
相关文章
|
3月前
|
自然语言处理 算法
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
54 0
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
|
3月前
|
算法 Java
HanLP — HMM隐马尔可夫模型 -- 维特比(Viterbi)算法 --示例代码 - Java
HanLP — HMM隐马尔可夫模型 -- 维特比(Viterbi)算法 --示例代码 - Java
39 0
|
3月前
|
算法 Python
Aho-Corasick 算法 AC自动机实现
Aho-Corasick 算法 AC自动机实现
62 0
|
自然语言处理 算法
中文分词算法工具hanlp源码解析
词图指的是句子中所有词可能构成的图。如果一个词A的下一个词可能是B的话,那么A和B之间具有一条路径E(A,B)。一个词可能有多个后续,同时也可能有多个前驱,它们构成的图我称作词图。
1693 0
|
自然语言处理 算法 程序员
自然语言处理工具hanlp关键词提取图解TextRank算法
TextRank是在Google的PageRank算法启发下,针对文本里的句子设计的权重算法,目标是自动摘要。它利用投票的原理,让每一个单词给它的邻居(术语称窗口)投赞成票,票的权重取决于自己的票数。这是一个“先有鸡还是先有蛋”的悖论,PageRank采用矩阵迭代收敛的方式解决了这个悖论。
1607 0
|
人工智能 自然语言处理 算法
hanlp源码解析之中文分词算法详解
词图指的是句子中所有词可能构成的图。如果一个词A的下一个词可能是B的话,那么A和B之间具有一条路径E(A,B)。一个词可能有多个后续,同时也可能有多个前驱,它们构成的图我称作词图。
2860 0
|
自然语言处理 算法 Java
HanLP 关键词提取算法分析详解
给定若干个句子,提取关键词。而TextRank算法是 graphbased ranking model,因此需要构造一个图,要想构造图,就需要确定图中的顶点如何构造,于是就把句子进行分词,将分得的每个词作为图中的顶点。
1611 0
|
自然语言处理 算法 Java