【算法】AC自动机的优化:增量更新与删除

news2024/11/19 8:23:50

一、概述

AC自动机(Aho-Corasick Automation)是著名的多模匹配算法,源于贝尔实验室,并且在实际应用中得到广泛的引用,且具有以下特点:

  • 只需要扫描一次文本,即可获取所有匹配该文本的模式串
  • 复杂度O(n)
  • 以树的结构进行存储
  • 通过Fail节点和Fail指针来提高匹配效率

对于AC自动机的具体实现,感兴趣可以自行搜索。

但是在实际应用场景中,AC自动机不仅仅只考虑匹配模式,还要考虑其模式串数据源的处理,比如模式串数据源的频繁变动(更新or移除数据),针对这样的情况下如果不断地对AC检测树进行推倒重建,在性能上消耗是十分庞大的。

因此,基于这样的场景,我们需要支持动态、快速、便捷地对已生成的AC检测树进行数据的插入、删除。

二、原理

原理很好理解,即:

  1. 新增节点自前往后遍历,删除节点自后往前遍历,逐一获取需要新增/删除的节点
  2. 更新指向该节点的Fail指针
  3. 更新该节点指向的Fail节点
  4. 在AC检测树中合并/移除该节点

在第2步中,原来的AC检测树的节点是不记录相关信息的,因此需要引入一个列表来管理该信息。

1. 新增节点

新增节点,即需要将新增加需要匹配的模式串源数据合并到已构建好的AC检测树中。

假定新增模式串为Z,已构建的AC检测树为AC_TREE

  • 判断ZAC_TREE的中最长前缀,并记录最长前缀的最后一个节点NODE
  • NODE作为遍历起点并开始遍历Z
    • 为新字符NEW_CHAR创建节点NEW_NODE并添加到NODE
    • 沿着NODE的Fail指针向上查找,直至找到某个节点下的子节点是NEW_CHAR,记录该节点的子节点为NEW_FAIL_NODE(如果没找到,则NEW_FAIL_NODE = ROOT
    • 更新NEW_NODE的Fail节点为NEW_FAIL_NODE
    • 获取Fail节点为NODE的节点列表REVER_NODE_LIST,并遍历
      • 判断REVER_NODE_LIST中节点的子节点REVER_CHILD_NODE的值是否有NEW_CHAR,如果有,则将REVER_CHILD_NODE的Fail节点设置为NEW_NODE,并更新NEW_NODEREVER_NODE_LIST
    • 设置NEW_NODE的其他属性

图文说明:

【初始状态】
在这里插入图片描述
【新增模式串后:ers】
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 删除节点

删除节点,即需要将某些模式串源数据已构建好的AC检测树中移除。

假定移除的模式串为Z,已构建的AC检测树为AC_TREE

  • 判断ZAC_TREE的位置,找到Z最后一个字符在AC_TREE中的节点NODE(如果不存在该模式串则跳出处理流程)
  • NODE作为遍历起点并开始反向遍历Z
    • 判断NODE是否存在其他子节点,有则跳出循环
    • 获取Fail节点为NODE的节点列表REVER_NODE_LIST,并遍历
      • REVER_NODE_LIST中的节点REVER_NODE的Fail节点设置为NODE的Fail节点
      • 更新NODEREVER_NODE_LIST
    • 更新NODE的Fail节点为NULL
    • 删除NODE,并更新NODE的父节点PARENT_NODE的子节点列表

图文说明:

【初始状态】
在这里插入图片描述

【删除模式串后:ers】
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、实现流程

P.S. 通过伪代码形式给出主要代码流程

// 新增节点
AddNode(list[str_1..str_n])
	for i ← 0 to n-1 do
    	str ←  list[i]; pos ← 0; node ← root
    	for j ← pos to str.len() do
    		pos ← j
    		node ← node.childNodeList[str[pos]]
   		
   		now_node ← node 
   		for j ← pos to str.len() do
   			chr ← str[j]
   			 // 查找Fail节点
   			fail_node ← GetFailNode(now_node, chr)
   			
   			// 创建新节点
   			next_node  ← CreateNewNode(now_node, chr) 
   			next_node, fail_node ← SetFailNode(next_node, fail_node)
   			
   			// 处理需要指向该节点的Fail指针
   			for k ← 0 to now_node.reverseFailNodeList do
   				tmp_node ← now_node.reverseFailNodeList[k][chr]
   				tmp_node , next_node ← SetFailNode(tmp_node , next_node)
   				now_node. reverseFailNodeList[k].childNodeList[chr] ← tmp_node 
   			

// 删除节点
DelNode(list[str_1..str_n])
	for i ← 0 to n-1 do
    	str ←  list[i]; pos ← 0; node ← root; nodeList  ← []
    	for j ← pos to str.len() do
    		pos ← j
    		nodeList.add(node)
    		node ← node.childNodeList[str[pos]]
    	
    	nodeList.reverse()
    	for j ← pos to str.len() do
    		now_node ← nodeList[j]
			fail_node ← now_node.faliNode
			// 处理指向该节点的所有Fail指针
			for k ← 0 to now_node.reverseFailNodeList do
				tmp_node ← now_node.reverseFailNodeList[k][chr]
   				tmp_node , next_node ← SetFailNode(tmp_node , next_node)
   				now_node. reverseFailNodeList[k].childNodeList[chr] ← tmp_node
   			
   			// 处理当前节点的Fail节点
  			now_node, tmpFail_node← SetFailNode(now_node, NULL) 

			// 删除该节点
			delete now_node.parentNode.childNodeList[now_node.char]
			

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小红书根据关键词取商品列表 API 返回值说明

小红书根据关键词取商品列表的API返回值通常包含与搜索请求相关的商品列表信息。这些信息包括匹配到的商品列表、商品详情、排序方式等。以下是一个简化的示例,展示了小红书根据关键词取商品列表API可能返回的JSON格式数据:获取调用详情链接 item_searc…

王道机试C++第8章递归与分治 Day35和蓝桥杯两道真题程序

第 8 章 递归与分治 递归是指:函数直接或间接调用自身的一种方法,通常可把一个复杂的大型问题层层转化为与原问题相似但规模较小的问题来求解。 递归策略只需少量的程序就可描述解题过程所需的多次重复计算,因此大大减少了程序的代码量。 8.…

一个命令查看自己的WIFI密码

一个命令查看自己的WIFI密码 忘记wifi密码怎么办?一个命令查看自己的wifi密码。 一、打开命令行 使用快捷键“WinR”,打开运行窗口,输入“cmd”后回车即可。 二、输入命令network shell命令 输入命令network shell,简称netsh&…

Android 面试题及答案整理,最新面试题

Android中Intent的作用及分类 Intent在Android中用作组件之间传递信息,它可以用于启动活动(activity)、服务(service)和发送广播(broadcast)。Intent主要分为显式Intent和隐式Intent两大类。 1、显式Intent: 直接指定了要启动的组件名称(比如…

【网络】详解HTTPS及探究加密过程

目录 一、什么是HTTPS1、加密解密是什么2、为什么要加密3、常见的加密方式1、对称加密2、非对称加密 二、探究HTTPS如何实现加密1、方案一----只使用对称加密2、方案二----只使用非对称加密3、方案三----双方都使用非对称加密4、方案四----非对称加密 对称加密5、中间人攻击6、…

企业微信 API 接口调用教程:深入解析企业微信 API 的用法

本文通过 access_token 凭证的方式来讲解怎么调用 企业微信 API,并一步步介绍如何获取企业微信 API 的 corpsecret、corpid、access_token 凭证以及怎么向企业微信的应用发送消息。 企业微信 API 在线地址为:qiyeweixin.apifox.cn/ ,这个在线…

STL:List从0到1

🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN&…

IO流(5)——转换流

不同编码读取出现乱码的问题 解决方法 字符输入转换流(InputStreamReader)

pytorch 入门基础知识(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向:计算机视觉,自然语言,语音识别。 机器学习核心组件:1 数据集(data),2 前向传播的model(net),3 目标函数(loss), 4 调整模型参数和优化函数的算法…

【Linux-网络编程】

Linux-网络编程 ■ 网络结构■ C/S结构■ B/S结构 ■ 网络模型■ OSI七层模型■ TCP/IP四层模型 ■ TCP■ TCP通信流程■ TCP三次握手■ TCP四次挥手 ■ 套接字:socket 主机IP 主机上的进程(端口号)■ TCP传输文件 ■ 网络结构 ■ C/S结构…

Java学习记录(二十二)反射,动态代理

反射获取class对象的三种方式 1、Class.forName(全类名) 2、类名.class 3、对象.getClass() 实现代码如下: package com.itazhang.Demo1;public class MyReflectDemo1 {public static void main(String[] args) throws ClassNotFoundException {//第一种获取cl…

Nacos源码流程图

1.Nacos1.x版本服务注册与发现源码 流程图地址:https://www.processon.com/view/link/634695eb260d7157a7bc6adb 2.Nacos2.x版本服务注册与发现源码 流程图地址:https://www.processon.com/view/link/634695fb260d7157a7bc6ae0 3.Nacos2.x版本GRPC…

【Linux进程信号】信号的发送与保存

【Linux进程信号】信号的发送与保存 目录 【Linux进程信号】信号的发送与保存阻塞信号1. 信号其他相关常见概念2. 在内核中的表示3. sigset_t4. 信号集操作函数sigprocmasksigpendingsignal测试这几个系统调用接口 进程地址空间第三讲捕捉信号1. 内核如何实现信号的捕捉2. siga…

Jenkins 面试题及答案整理,最新面试题

Jenkins中如何实现持续集成与持续部署? Jenkins通过自动化构建、测试和部署应用程序来实现持续集成与持续部署(CI/CD)。这个过程包括以下步骤: 1、源代码管理: Jenkins支持与多种版本控制系统集成,如Git、…

Java项目:55 springboot基于SpringBoot的在线视频教育平台的设计与实现015

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 在线视频教育平台分为管理员和用户、教师三个角色的权限模块。 管理员所能使用的功能主要有:首页、个人中心、用户管理、教师管理、课程信…

绝赞春招拯救计划 -- 操作系统,组成原理,计网

进程和线程 进程 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程 线程 进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以…

关于Apache

文章目录 一、httpd 安装组成1、常见http 服务器程序2、apache 介绍和特点2.1 关于apache2.1.1 apache 功能2.1.2 apache特性 2.2 MPM multi-processing module 工作模式2.2.1 prefork2.2.2 worker2.2.3 event 3、Httpd 安装和相关文件3.1 包安装httpd并启动httpd服务3.1.1 版本…

配置vscode环境极简版(C/C++)(图文)

前言 众所周知,vscode是一个代码编辑器,不能直接编译运行我们敲的代码,必须提前配置好环境,而这也是劝退一众小白的一大重要因素,下面我想以一种提纲挈领的方式带大家走一遍从配置环境到运行实操代码的全过程。 安装…

用户故事到需求实例化

用户故事 用户故事是敏捷开发方法中的核心概念之一,它提供了一种简洁的方式来描述软件功能需求,同时强调这些功能为用户或业务带来的价值。用户故事通常是由用户、产品经理或业务分析师编写的简短描述,用于与开发团队沟通需求,并…

面向对象(下)

目录 01、static1.1、static的使用1.2、static应用举例1.3、单例(Singleton)设计模式 02、main方法的语法03、类的成员之四:代码块04、关键字:final05、抽象类与抽象方法5.1、多态的应用:模板方法设计模式(TemplateMethod) 06、接口(interfac…