多模式字符串匹配 - AC自动机原理及实现

AC自动机是一种多模匹配算法,源自1975年的贝尔实验室。它结合了字典树(Trie树)和失配指针,可以在字符串中高效查找多个子串。构造AC自动机包括构建字典树、设置失配指针和进行AC匹配过程。通过字典树存储词汇,失配指针对应最长后缀,匹配过程中通过失配指针快速定位,实现O(m+n)的时间复杂度。

给一个字符串,找出字符串中指定的单词,如果是单个词,我们可以通过暴力解法,从头开始遍历字符串,判断是否存在指定的单词,当然还有一种更优的解法,那就是KMP算法,通过构造next数组,保存匹配的中间状态,在查找单词失败时,不用退回到字符串开头重新进行匹配,实现了O(m+n)的时间复杂度。这种在一个字符串中找一个单词的匹配成为单模式匹配,如果要在一个字符串中寻找多个子串了,那就是将要谈到的多模式匹配,如何实现呢?对每个子串进行KMP匹配,当然也可以。但是有一种更为优秀的多模式匹配算法,那就是AC自动机算法。

Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。

AC自动机算法可以理解为树形的KMP算法,不过不了解KMP也不影响学习AC自动机算法,不过需要了解字典树和多叉树的广度遍历算法。

字典树

什么是Trie树?

Trie树,也叫字典树,是一种多叉树形数据结构,每个节点保存一个字符,一条路径上的所有节点保存一个字符串。以{"hers", "he", "his", "she"}构造的字典树为例:
在这里插入图片描述

如上图所示,每个单词在一条路径上,绿色节点表示该节点是一个单词的尾字符。其中root节点不存储任何字符。

如何构造一个字典树?

看上面的图应该已经知道如何构造一个Trie树了,那代码如何实现呢?首先定义节点的结构体:

struct TrieNode {
   
   
    TrieNode*                           fail{
   
   nullptr}; // 失配指针
    std::vector<int>                    exists;        // 该节点出是否存在完整单词
    std::unordered_map<char, TrieNode*> childs;        // 子节点,用 hash 表方便快速查询
};

其中:

exists字段用来存储以该节点字符结尾单词的长度,如单词"hers",则该节点exists = {4},用这个长度我们便可以截取出提取出字符串对应的该单词,而不用存储整个单词。

fail指针表示失配指针,后续将匹配过程会理解它。

childs用来存储子节点,为了快速定位子节点,我们用hash表存储子节点,键值为字符,value值为子节点其它信息

通过给定的多个单词构造Trie树代码如下:


TrieNode* buildTrie(const std::vector<std::string>& words) {
   
   
    if (words.empty()) {
   
   
        cout << "[buildTrie] words is empty" << endl;
        return nullptr;
    }
    TrieNode* root = new TrieNode();
    for (auto& word : words) {
   
   
        TrieNode* tmp = root;
        for (char ch : word) {
   
   
            if (tmp->childs.find(ch) == tmp->childs.end()) {
   
   
                tmp->childs[ch] = new TrieNode();
            }
            tmp = tmp->childs[ch];
        }
        tmp->exists.emplace_back(word.size());
    }
    return root;
}

不要忘记在每个单词的尾字符节点存储单词的长度

失配指针

失配指针的含义

先看已构造好失配指针的图,后续讲如何构造失配指针

在这里插入图片描述

如图所示,每个节点的失配指针都指向指定节点,有的指向root节点,有的指向字符节点,那失配指针代表什么呢?可以理解为:
如果节点A的失配指针指向节点B,那么就表示:节点B结尾的字符串是节点A结尾的字符串的最长后缀:

  • 节点[her]s.fail = ss是该字典树中hers的最长后缀
  • 节点[sh]e.fail = [h]ehe是该字典树中she的最长后缀
  • 节点[h]e.fail = roothe在该字典树中没有最长后缀

可以自己体会一下😂

构造失配指针

代码如下:


TrieNode* initAhoCorasick(const std::vector<std::string>& words) {
   
   
    TrieNode* root 
内容概要:本文介绍了一个关于超声谐波成像中幅度调制聚焦超声所引起全场位移和应变的分析模型,并提供了基于Matlab的代码实现。该模型旨在精确模拟和分析在超声谐波成像过程中,由于幅度调制聚焦超声作用于生物组织时产生的力学效应,包括全场的位移与应变分布,从而为医学成像和治疗提供理论支持和技术超声谐波成像中幅度调制聚焦超声引起的全场位移和应变的分析模型(Matlab代码实现)手段。文中详细阐述了模型构建的物理基础、数学推导过程以及Matlab仿真流程,具有较强的理论深度与工程应用价值。; 适合人群:具备一定声学、生物医学工程或力学背景,熟悉Matlab编程,从事医学成像、超声技术或相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于超声弹性成像中的力学建模与仿真分析;②支持高强度聚焦超声(HIFU)治疗中的组织响应预测;③作为教学案例帮助理解超声与组织相互作用的物理机制;④为相关科研项目提供可复用的Matlab代码框架。; 阅读建议:建议读者结合超声物理和连续介质力学基础知识进行学习,重点关注模型假设、偏微分方程的数值求解方法及Matlab实现细节,建议动手运行并修改代码以加深理解,同时可拓展应用于其他超声成像或治疗场景的仿真研究。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值