Aho Corasick Algorithm

文章目录

    • 前言
    • 介绍
    • 实现
    • 参考

前言

Aho Corasick Algorithm又叫AC自动机,该算法是一个匹配算法,用来匹配文本Text中多个patterns分别出现的次数;

我们定义npatterns的总长度;mText的长度;

问题:在ahishershe文本中找出以下"he", "she", "hers", "his"各个patterns出现的次数;

最直接的暴力解法时间时间复杂度为O(n*m),如果采用KMP Algorithm,会把时间度降低为O(n+m),但是这是在单一的pattern的情况下,在k个pattern的情况下,应该成倍的增长m,时间复杂度为O(n+k*m),使用Aho Corasick Algorithm可以把时间优化为O(n+m+z),其中z表示出现的次数;

介绍

AC自动机利用的前缀树Trie这一数据结构;前缀树是一种很简单的结构,其构造如下:

我们可以构建Trie然后使用窗口进行暴力搜索,其时间复杂度为O(m*max(L)),这里面的max(L)Trie的深度;

这里面可以优化的一个点是,我们可以利用不匹配词的最长后缀作为词的前缀去优化查找,而不是不匹配就重新从root开始;找到最长的公共后缀将保证我们不需要再次检查那么多字符串,并且在每次不匹配之后我们都会重复上一步骤;

如图所示:

这些指向最长后缀的节点的链接被称为suffix linksfailure links,在匹配不成功的时候,若有最长的后缀节点,指向最长的后最节点,若没有则指向root进行重新查找;

假设在一颗Trie上有字符串w,x,...,其中xw的最长的后缀,所以有红线连接wa,若存在waxa,这xa也是wa的最长后缀,也用红线连接;

xa不存在,就去找除了x以外的和w的最大公共后缀,如果发现y符合条件,可以对y进行匹配;如果y不符合条件,就接着找除了x,y以外的和w的最大公共后缀,依次进行;

其次需要优化的一点输出环节,如果pattern1pattern2的后缀的情况下,我们可能会忽略掉pattern1,所以在寻找最大后缀时需要进行判断,如果最大后缀结点是pattern,就用output link连接原结点指向该结点,在以该结点为原结点去连接,直到root;如果不是就判断该结点的最大后缀结点是否是pattern,再用原结点去连接这一个结点;

在文本sting中,遍历到i的时候,可以发现sti的最大后缀ti并不是pattern,于是就找ti的最大后缀i,是pattern,于是用蓝色的线把stii结合起来;这里加粗了这一过程;

在遍历到n的时候,发现tinin都是pattern,依次进行连接

这时时间复杂度为O(m+z),其中z表示pattern的出现次数;

实现

下面是python实现的代码:

from collections import defaultdict


class ahocorasick:
    def __init__(self, words):
        self.max_states = sum([len(word) for word in words])
        self.max_characters = 26
        self.out = [0] * (self.max_states + 1)
        self.fail = [-1] * (self.max_states + 1)
        self.goto = [[-1] * self.max_characters for _ in range(self.max_states + 1)]

        for i in range(len(words)):
            words[i] = words[i].lower()

        self.words = words
        self.states_count = self.__build_matching_machine()

    def __build_matching_machine(self):
        k = len(self.words)

        states = 1
        for i in range(k):
            word = self.words[i]
            current_state = 0
            for character in word:
                ch = ord(character) - 97
                if self.goto[current_state][ch] == -1:
                    self.goto[current_state][ch] = states
                    states += 1

                current_state = self.goto[current_state][ch]
            self.out[current_state] |= (1 << i)
        for ch in range(self.max_characters):
            if self.goto[0][ch] == -1:
                self.goto[0][ch] = 0
        queue = []
        for ch in range(self.max_characters):
            if self.goto[0][ch] != 0:
                self.fail[self.goto[0][ch]] = 0
                queue.append(self.goto[0][ch])
        while queue:
            state = queue.pop(0)
            for ch in range(self.max_characters):
                if self.goto[state][ch] != -1:
                    failure = self.fail[state]
                    while self.goto[failure][ch] == -1:
                        failure = self.fail[failure]

                    failure = self.goto[failure][ch]
                    self.fail[self.goto[state][ch]] = failure
                    self.out[self.goto[state][ch]] |= self.out[failure]
                    queue.append(self.goto[state][ch])

        return states

    def __find_next_state(self, current_state, next_input):
        answer = current_state
        ch = ord(next_input) - 97
        while self.goto[answer][ch] == -1:
            answer = self.fail[answer]

        return self.goto[answer][ch]

    def search_words(self, text):
        text = text.lower()
        current_state = 0

        result = defaultdict(list)

        for i in range(len(text)):
            current_state = self.__find_next_state(current_state, text[i])

            if self.out[current_state] == 0: continue

            for j in range(len(self.words)):
                if (self.out[current_state] & (1 << j)) > 0:
                    word = self.words[j]
                    result[word].append(i - len(word) + 1)

        return result


if __name__ == "__main__":
    words = ["he", "she", "hers", "his"]
    text = "ahishershe"

    aho_chorasick = ahocorasick(words)

    result = aho_chorasick.search_words(text)

    for word in result:
        for i in result[word]:
            print("Word", word, "appears from", i, "to", i + len(word) - 1)

参考

Aho Corasick Algorithm (opengenus.org)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/233999.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp实战 —— 可滚动区域 scroll-view (自适配高度,下拉刷新)

自适配高度 自定义的顶部导航栏&#xff0c;可参考博文 https://blog.csdn.net/weixin_41192489/article/details/134852124 如图可见&#xff0c;在页面滚动过程中&#xff0c;顶部导航栏和底栏未动&#xff0c;仅中间的内容区域可滚动。 整个页面的高度设置为 100%&#xf…

基于以太坊的智能合约开发Solidity(事件日志篇)

//声明版本号&#xff08;程序中的版本号要和编译器版本号一致&#xff09; pragma solidity ^0.5.17; //合约 contract EventTest {//状态变量uint public Variable;//构造函数constructor() public{Variable 100;}event ValueChanged(uint newValue); //事件声明event Log(…

深度学习 | Pytorch深度学习实践 (Chapter 12 Basic RNN)

十二、Basic RNN —— 实际上就是对线性层的复用 使用RNN最重要的两点&#xff1a; 了解序列数据的维度&#xff1b;循环过程所用的权重共享机制&#xff1b; 一般就是自己写个循环&#xff0c;权重层重复用就行了&#xff1b; 回顾&#xff1a;-----------------------------…

09--面向对象OOP--04

1、关键字&#xff1a;static 1.1 什么是static关键字 它可以用来修饰的成员变量和成员方法&#xff0c;被修饰的成员是属于类的&#xff0c;而不是单单是属 于某个对象的。也就是说&#xff0c;既然属于类&#xff0c;就可以不靠创建对象来调用了。 使用范围&#xff1a; 在…

高级Linux监控堡垒机学习指南

高级Linux监控堡垒机学习指南 在现代复杂的网络环境中&#xff0c;安全性和监控是系统管理的核心关注点。Linux监控堡垒机作为一种安全管理工具&#xff0c;不仅可以追踪系统活动&#xff0c;还能提供对服务器和网络资源的高级监控。本文将深入探讨高级Linux监控堡垒机的学习内…

Grafana系列-Loki-基于日志实现告警

系列文章 Loki 系列文章 前言 实际应用中除了基于 Metrics 告警, 往往还有基于日志的告警需求, 可以作为基于 Metrics 告警之外的一个补充. 典型如基于 NGINX 日志的错误率告警.本文将介绍如何基于 Loki 实现基于日志的告警. 本文我们基于以下 2 类实际场景进行实战演练: …

【Java实现百钱买百鸡的两种写法】

Java实现百钱买百鸡的两种写法 Java双重嵌套for循环实现百钱买百鸡的写法&#xff08;一&#xff09;Java三重嵌套for循环实现百钱买百鸡的写法&#xff08;二&#xff09; Java双重嵌套for循环实现百钱买百鸡的写法&#xff08;一&#xff09; //定义一个记录循环次数变量int …

Redis rdb源码解析

前置学习&#xff1a;Redis server启动源码-CSDN博客 1、触发时机 1、执行save命令--->rdbSave函数 2、执行bgsave命令--->rdbSaveBackground函数或者&#xff08;serverCron->prepareForShutdown&#xff09; 3&#xff0c;主从复制-->startBgsaveForReplication…

医疗大模型产品收集

在之前的一篇文章【LLM大模型中文开源数据集集锦&#xff08;三&#xff09;】采集到了一些医疗大模型所使用的数据&#xff0c;数据中比较多的是竞赛中出现训练集&#xff0c;对话语料居多。 大模型也出现好一阵子&#xff0c;一些医疗大模型产品化、开源模型也越来越多&#…

[LeetCode]-283. 移动零-1089. 复写零

目录 283. 移动零 描述 解析 代码 1089. 复写零 描述 解析 代码 283. 移动零 283. 移动零https://leetcode.cn/problems/move-zeroes/ 描述 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &…

C#网络应用程序(Web页面浏览器、局域网聊天程序)

目录 一、创建Web页面浏览器 1.示例源码 2.生成效果 二、局域网聊天程序 1.类 2.服务器端 3.客户端 一、创建Web页面浏览器 TextBox 控件用来输入要浏览的网页地址&#xff0c;Button控件用来执行浏览网页操作&#xff0c; WebBrowser控件用来显示要浏览的网页。这个控…

2023年11月10日 Go生态洞察:十四年Go的成长之路

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

最新接口自动化测试面试题

前言 前面总结了一篇关于接口测试的常规面试题&#xff0c;现在接口自动化测试用的比较多&#xff0c;也是被很多公司看好。那么想做接口自动化测试需要具备哪些能力呢&#xff1f; 也就是面试的过程中&#xff0c;面试官会考哪些问题&#xff0c;知道你是不是真的做过接口自…

推荐一个界面设计软件aardio,配合python三分钟制作一个小软件。【批量doc文件转docx文件】

文章目录 前言一、aardio软件代码二、python代码总结 前言 aardio这个软件不多说&#xff0c;好用方便。 一、aardio软件代码 import win.ui; /*DSG{{*/ mainForm win.form(text"批量doc文件转docx文件";right623;bottom171) mainForm.add( button{cls"butto…

前端面经(字节)------持续更新

1. JS数据类型有哪些&#xff1f; JS数据类型分为两类&#xff1a; 基本数据类型&#xff08;也叫简单数据类型&#xff09;&#xff0c;包含7种类型&#xff0c;分别是&#xff1a; Number&#xff0c;String&#xff0c;Boolean&#xff0c;Null&#xff0c;Undefined&…

perl处理base64、md5、SHA-1、SHA-256的计算

使用perl可以进行base64、md5、SHA-1、SHA-256的计算&#xff0c;使用也非常方便&#xff0c;下面是示例代码&#xff1a; #! /usr/bin/perl use v5.14; use MIME::Base64; use Digest;my $test_str hello world;# 测试base64 say encode_base64($test_str);# 测试md5 my $md…

Rsync+Sersync

服务器相关参数 源服务器 192.168.17.101 目标服务器&#xff08;同步到的服务器&#xff09; 192.168.17.103 ##目标服务器配置 ###1、配置rsync服务 1、安装rsync yum -y install rsync 2、配置rsync vim /etc/rsyncd.conf 配置文件内容 uid root gid root use c…

Jenkins+Ant+Jmeter接口自动化集成测试

一、Jenkins安装配置 1、安装配置JDK1.6环境变量&#xff1b; 2、下载jenkins.war&#xff0c;放入C:\jenkins目录下&#xff0c;目录位置随意&#xff1b; Jenkins启动方法&#xff1a; cmd进入Jenkins目录下&#xff0c;执行java -jar jenkins.war 浏览器输入&#xff1a;l…

网络协议疑点记录

1.RIP, OSPF,BGP 首先什么是自治系统:治系统就是几个路由器组成了一个小团体 ?,小团体内部使用专用的协议进行通信,而小团体和小团体之间也使用专用的协议进行通信。 IGP RIP 距离矢量路由算法,bellman-ford算法,每个路由节点知道全局的路由信息,通过和邻居交换信息得…

【MySQL进阶】索引使用

一、索引使用 1.验证索引效率 tb_sku 这张表中准备了 1000w 的记录。 我用夸克网盘分享了「1000w的模拟数据」链接&#xff1a;https://pan.quark.cn/s/15cf665202b2 这张表中id为主键&#xff0c;有主键索引&#xff0c;而其他字段是没有建立索引的。 我们先来查询其中的…