Java数据结构之AC自动机算法的实现

Java数据结构之AC自动机算法的实现

本文将详细讲解AC自动机算法在Java中的实现方法和原理,同时提供两个示例进行说明,使读者能够深入了解该算法并学会如何使用。

什么是AC自动机算法

AC自动机(Aho-Corasick Automaton)是多模式匹配的一种经典算法,其基本思路是将多个模式串构建成一颗“字典树”,然后对输入的文本串进行扫描匹配。相比于简单的暴力匹配方法,AC自动机算法可以在时间复杂度为O(n)的情况下实现多模式匹配,在诸如关键词过滤、字符串匹配等场景中具有很高的应用价值。

AC自动机算法的实现主要分为以下三个步骤:

  1. 构建trie树:将所有模式串构建成一个trie树;
  2. 对trie树进行预处理:为每个节点计算出其在trie树上的fail指针,并将所有模式串的状态存储到一个状态集合中;
  3. 使用AC自动机模式匹配算法:对输入的文本串进行扫描匹配,每遇到一个字符就在trie树上进行前缀匹配,并跟随其相应的fail指针回溯到其下一个状态,直到匹配成功或达到文本串的末尾。

Java实现

构建trie树

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

    public TrieNode() {
        this.children = new HashMap<>();
        this.isEndOfWord = false;
        this.patterns = new HashSet<>();
    }
}

class Trie {
    private TrieNode root;

    /**
     * Initialize your data structure here.
     */
    public Trie() {
        root = new TrieNode();
    }

    /**
     * Inserts a word into the trie.
     */
    public void insert(String word) {
        TrieNode curr = root;
        for (char c : word.toCharArray()) {
            curr.children.putIfAbsent(c, new TrieNode());
            curr = curr.children.get(c);
        }
        curr.isEndOfWord = true;
        curr.patterns.add(word); // 记录该节点所包含的所有模式串
    }
}

计算fail指针

class ACNode {
    char c; // 该节点代表的字符
    ACNode fail; // fail指针
    Map<Character, ACNode> children; // 子节点
    Set<String> patterns; // 存储该节点包含的所有模式串

    // 计算该节点的fail指针
    public void computeFailPointer() {
        Queue<ACNode> queue = new LinkedList<>();
        for (Map.Entry<Character, ACNode> entry : children.entrySet()) {
            char c = entry.getKey();
            ACNode child = entry.getValue();
            child.fail = this;
            queue.offer(child);
        }
        while (!queue.isEmpty()) {
            ACNode curr = queue.poll();
            for (Map.Entry<Character, ACNode> entry : curr.children.entrySet()) {
                char c = entry.getKey();
                ACNode child = entry.getValue();
                ACNode fail = curr.fail;
                while (fail != null && !fail.children.containsKey(c)) {
                    fail = fail.fail;
                }
                child.fail = fail == null ? this : fail.children.get(c);
                child.patterns.addAll(child.fail.patterns);
                queue.offer(child);
            }
        }
    }
}

class AC {
    private ACNode root;

    public AC(Trie trie) {
        root = new ACNode();
        for (Map.Entry<Character, TrieNode> entry : trie.root.children.entrySet()) {
            char c = entry.getKey();
            TrieNode child = entry.getValue();
            ACNode acChild = new ACNode();
            acChild.c = c;
            root.children.put(c, acChild);
            acChild.patterns.addAll(child.patterns);
            acChild.fail = root;
        }
        root.fail = root;
    }

    // 计算AC自动机的所有fail指针
    public void computeFailPointers() {
        root.computeFailPointer();
    }
}

AC自动机模式匹配

class ACAutomaton {
    private AC ac;

    public ACAutomaton(Trie trie) {
        ac = new AC(trie);
        ac.computeFailPointers();
    }

    public List<String> findAllOccurrences(String text) {
        List<String> res = new ArrayList<>();
        ACNode curr = ac.root;
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            while (curr != null && !curr.children.containsKey(c)) {
                curr = curr.fail;
            }
            if (curr == null) {
                curr = ac.root;
            } else {
                curr = curr.children.get(c);
                curr.patterns.forEach(res::add);
            }
        }
        return res;
    }
}

示例说明

示例一

假设有字符串集合{“she”, “he”, “say”, “sh”, “shr”, “her”},并且要在下面这段文本中找出所有的模式串:

she says he has her shrubs

对该文本进行扫描匹配,最终找出的模式串是:

she, he, her, shr, say

示例二

假设有字符串集合{“cc”, “ccc”, “cccc”},并且要在下面这段文本中找出所有的模式串:

abccccde

对该文本进行扫描匹配,最终找出的模式串是:

cc, ccc, cccc

总结

以上是AC自动机算法在Java中的实现方法和原理,本文详细讲解了AC自动机的实现过程和三个步骤,并同时提供了两个示例,希望能够帮助读者深入了解该算法并学会使用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构之AC自动机算法的实现 - Python技术站

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

相关文章

  • java编程队列数据结构代码示例

    下面是“Java编程队列数据结构代码示例”的完整攻略。 什么是队列 队列是一种有序的数据结构,特点是先进先出(FIFO)。队列中不管是插入操作还是删除操作,都是在队列的两端进行的,插入操作在队列的尾部进行,删除操作在队列的头部进行。队列的一个重要用途是在计算机的操作系统中,实现进程和所有需要等待资源的实体之间的交互。 队列的实现 队列数据结构可以采用数组或链…

    数据结构 2023年5月17日
    00
  • Redis的六种底层数据结构(小结)

    Redis的六种底层数据结构(小结) 简介 Redis是一种基于内存的高效键值存储数据库,它支持六种不同的数据结构来存储数据。这些结构旨在提供高速、灵活和功能强大的一系列功能。在学习Redis时,了解这些数据结构可以帮助您更好地使用Redis并更好地解决您的问题。 Redis的六种底层数据结构 Redis支持以下六种不同的数据结构: String (字符串)…

    数据结构 2023年5月17日
    00
  • C语言 数据结构堆排序顺序存储(升序)

    C语言 数据结构堆排序顺序存储(升序)攻略 1. 堆排序概述 堆排序是一种常见的排序算法,通过构建最大堆或最小堆来实现排序。本文介绍的是使用顺序存储方式实现的最大堆排序,也就是升序排序。 2. 最大堆的定义和实现 最大堆指的是堆结构中父节点的值大于子节点的值,根节点的值最大。对于一棵完全二叉树,若父节点的下标为i,则其左子节点的下标为2i+1,右子节点的下标…

    数据结构 2023年5月17日
    00
  • C语言二叉树的概念结构详解

    C语言二叉树的概念结构详解 什么是二叉树 二叉树是一种特殊的树形结构,它由一个根节点和若干个子树组成,其中每个节点都最多有两个子节点,分别称为它的左子节点和右子节点。 二叉树的结构 一个二叉树通常由以下几个结构组成: 数据域:存储节点所包含的数据 左节点:节点左侧的子节点,如果为空节点,则表示当前节点没有左子树 右节点:节点右侧的子节点,如果为空节点,则表示…

    数据结构 2023年5月17日
    00
  • python数据结构学习之实现线性表的顺序

    下面我来详细讲解一下“python数据结构学习之实现线性表的顺序”的完整攻略。 一、线性表的概念介绍 线性表是最基本、最常用的一种数据结构。线性表是由同类型的数据元素构成有序序列的抽象,常用的线性表有顺序表和链表两种结构。 顺序表就是用一段连续的物理空间依次存储一组类型相同的数据元素,同时在存储空间中,逻辑上相邻的两个元素,物理位置也相邻。 二、实现顺序表的…

    数据结构 2023年5月17日
    00
  • C语言数据结构 双向链表的建立与基本操作

    C语言数据结构 双向链表的建立与基本操作 双向链表的定义 双向链表是一种常见的线性数据结构,它由多个结点组成,每个结点有两个指针,一个指向前一个结点,一个指向后一个结点。对于一个双向链表,我们可以获得其第一个结点和最后一个结点的指针,也可以沿着链表从前往后或从后往前遍历链表的每个结点。 双向链表的建立 我们首先需要定义一个双向链表的结点类型,包括两个指针,一…

    数据结构 2023年5月17日
    00
  • C++ 二叉树的实现超详细解析

    C++ 二叉树的实现超详细解析 在本篇文章中,我们将详细讲解如何使用C++语言实现二叉树数据结构。我们将分为以下几个部分: 二叉树的定义 二叉树的基本操作 C++实现 1. 二叉树的定义 二叉树是一种树形数据结构,其中每个节点最多有两个子节点。二叉树有以下几个特点: 树中的每个节点最多有两个子节点 左子节点的键值比父节点的键值小 右子节点的键值比父节点的键值…

    数据结构 2023年5月17日
    00
  • 浅析Java 数据结构常用接口与类

    浅析 Java 数据结构常用接口与类 本文主要介绍 Java 中常用的数据结构接口和类,可以帮助读者了解和掌握常见的数据结构以及它们的实现方式,从而在日后的开发中使用它们,提高代码的效率和质量。 List 接口 List 接口是 Java 中常用的数据结构接口之一,它代表了一个有序的集合,集合中的每一个元素都可以通过其索引进行访问。List 接口的一些常用方…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部