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日

相关文章

  • Java编程中的性能优化如何实现

    下面是Java编程中的性能优化攻略,共分为四个步骤: 1. 定位瓶颈 性能优化的第一步是定位瓶颈,只有知道哪里出了问题才能有针对性地进行优化。我们可以使用一些工具来定位瓶颈,比如: JProfiler:一款功能强大的Java性能分析工具,在视图中可以观察到CPU使用率、内存占用、线程状态、对象创建等性能特征,帮助我们快速定位瓶颈。 Java Mission …

    Java 2023年5月24日
    00
  • Java对日期Date类进行加减运算、年份加减月份加减、时间差等等

    Java 8 提供了一组全新的日期和时间库,其中 LocalDate、LocalTime、LocalDateTime 用于代替旧的 Date、Calendar 等类。下面主要介绍 LocalDate 的日期加减、年份月份加减、时间差的处理方法。 日期加减 使用 plusDays(long daysToAdd) 方法可以对日期进行加操作,该方法返回一个新的日期…

    Java 2023年5月20日
    00
  • Java详解实现ATM机模拟系统

    Java详解实现ATM机模拟系统攻略 系统概述 该ATM机模拟系统是用Java语言实现的,包含了模拟受卡人身份认证、存款、取款等操作。此系统模拟银行的ATM机功能,可以满足普通用户的基本需求。 技术栈 Java:Java SE 8版本及以上 IDE:Eclipse, IntelliJ IDEA等 Maven:用于管理依赖 JUnit:用于单元测试 功能模块 …

    Java 2023年5月24日
    00
  • 什么是Java线程池?

    Java线程池是Java提供的一个用于管理和重复使用线程的机制。线程池将一组线程存储在内存中,当需要执行一些任务时,可以分配一个线程来处理任务,以提高性能和资源利用率。 Java线程池的使用攻略: 步骤1:创建一个线程池 Java线程池通常使用Executor工厂类来创建。 Executor提供了许多静态工厂方法来创建不同种类的线程池。其中,最常用的是Exe…

    Java 2023年5月11日
    00
  • Spring-webflux 响应式编程的实例详解

    Spring-webflux 响应式编程的实例详解 Spring-webflux 是 Spring Framework 5.0 中引入的新特性,它提供了一种基于响应式编程模型的 Web 开发方式。本文将详细讲解 Spring-webflux 响应式编程的实例详解,包括如何创建响应式 Web 应用程序、如何使用响应式路由、如何使用响应式数据访问等。 创建响应式…

    Java 2023年5月18日
    00
  • java实现的MD5摘要算法完整实例

    下面是关于“java实现的MD5摘要算法完整实例”的详细讲解。 什么是MD5摘要算法? MD5是一种常用的哈希算法,用于为任意长度的数据产生一个固定长度的散列值。因为MD5算法的散列值是固定长度的,所以经常用于检验数据的完整性和安全性。MD5算法的散列结果是一个128位的二进制数,通常用一个32位的16进制数表示。 MD5算法实现步骤 MD5算法的计算过程包…

    Java 2023年5月19日
    00
  • struts2.5+框架使用通配符与动态方法常见问题小结

    Struts2.5+框架使用通配符与动态方法常见问题 在使用Struts2.5+框架进行web开发过程中,经常会用到通配符和动态方法的方式进行访问,但在实际开发中,可能会遇到一些问题。下面我们就来详细讲解一下在使用通配符和动态方法时会遇到的常见问题,并提供一些解决方案。 通配符使用 通配符的作用是将不同的请求映射到同一个Action中进行处理。比如你有两个请…

    Java 2023年5月20日
    00
  • Spring Boot 2和Redis例子实现过程解析

    Spring Boot2和Redis例子实现过程解析 Redis是一个高性能的键值存储系统,常用于缓存、消息队列等场景。在Spring Boot应用程序中,我们可以使用Spring Data Redis来快速开发Redis相关的应用程序。本文将详细讲解Spring Boot2和Redis例子实现过程解析,并提供两个示例。 1. 添加Redis依赖 在pom.…

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