关于AC自动机结构详解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:
AC自动机的构建,分析优化,时间复杂度分析。


提示:以下是本篇文章正文内容,下面案例可供参考

一、AC自动机是什么?

AC自动机就是一个单词查找器,能够高效的查找单词是否存在在字典树里面。

二、自动机的构建。

字典树的构建过程是这样的,当要插入许多单词的时候,我们要从前往后遍历整个字符串,当我们发现当前要插入的字符其节点再先前已经建成,我们直接去考虑下一个字符即可,当我们发现当前要插入的字符没有再其前一个字符所形成的树下没有自己的节点,我们就要创建一个新节点来表示这个字符,接下往下遍历其他的字符。然后重复上述操作。

代码如下(示例):

struct Node {
    Node() : flag(false), fail(nullptr) {
        for(int i = 0; i < BASE; i++) next[i] = nullptr;
        return ;
    }
    string *s;
    bool flag;
    Node *next[BASE];
    Node *fail;
};

三、自动机朴素实现方法以及优化版本

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <unordered_set>
using namespace std;

#define BASE 26

struct Node {
    Node() : flag(false), fail(nullptr) {
        for(int i = 0; i < BASE; i++) next[i] = nullptr;
        return ;
    }
    string *s;
    bool flag;
    Node *next[BASE];
    Node *fail;
};

struct Trie {
public:
    Trie() = default;
    void insert(string s) {
        Node *p = &root;
        for(auto x : s) {
            int ind = x - 'a';
            if(p->next[ind] == nullptr) p->next[ind] = new Node();
            p = p->next[ind];
        }
        if(p->flag == false) {
            p->flag = true;
            p->s = new string(s);
        }
        return ;
    }

    void build_ac_1() {
        queue<Node *> q;
        // 根节点下面的节点, fail指针, 一定指向根节点
        for(int i = 0; i < BASE; i++) {
            if(root.next[i] == nullptr) {
                continue;
            }
            root.next[i]->fail = &root;
            q.push(root.next[i]);
        }
        while(!q.empty()) {
            Node *now = q.front(), *p;
            q.pop();
            for(int i = 0; i < BASE; i++) {
                if(now->next[i] == nullptr) continue;
                
                p = now->fail; // now -> p -> p'
                // 当父节点的fail指针下面也没有i, 那么就不停的向上找
                while(p && p->next[i] == nullptr) {
                    p = p->fail; // p -> p'
                }
                if(p) p = p->next[i];
                else p = &root;

                now->next[i]->fail = p;
                q.push(now->next[i]);
            }
        }
        return ;
    }
 // 优化版本建树 
    void build_ac() {
        queue<Node *> q;
        // 根节点下面的节点, fail指针, 一定指向根节点
        for(int i = 0; i < BASE; i++) {
            if(root.next[i] == nullptr) {
                root.next[i] = &root;
                continue;
            }
            root.next[i]->fail = &root;
            q.push(root.next[i]);
        }
        while(!q.empty()) {
            Node *now = q.front(), *p;
            q.pop();
            for(int i = 0; i < BASE; i++) {
                if(now->next[i] == nullptr) {
                    // 利用上空节点的作用, 即使为空, 也要利用上
                    now->next[i] = now->fail->next[i];
                    continue;
                }
                now->next[i]->fail = now->fail->next[i];
                q.push(now->next[i]);
            }
        }
        return ;
    }
//优化后版本匹配 
    unordered_set<string> match(string &s) {
        unordered_set<string> ret;
        Node *p = &root, *k;
        int cnt = 0;
        for(auto x : s) {
            int ind = x - 'a';
            // 不在有next数组是空了, 我们直接指过去
            p = p->next[ind];
            cnt++;
            k = p;
            while(k) {
                if(k->flag) ret.insert(*(k->s));
                k = k->fail;
            }
        }
        cout << "total operator : " << cnt << endl;
        return ret;
    }

    unordered_set<string> match_2(string &s) {
        unordered_set<string> ret;
        Node *p = &root, *k;
        for(auto x : s) {
            int ind = x - 'a';
            while(p && p->next[ind] == nullptr) p = p->fail;
            if(p) p = p->next[ind];
            else p = &root;
            
            k = p;
            while(k) {
                if(k->flag) ret.insert(*(k->s));
                k = k->fail;
            }
        }
        return ret;
    }


    unordered_set<string> match_1(string &s) {
        unordered_set<string> ret;
        for(int i = 0; i < s.size(); i++) {
            Node *p = &root;
            for(int j = i; j < s.size(); j++) {
                int ind = s[j] - 'a';
                if(p->next[ind] == nullptr) break;
                p = p->next[ind];
                if(p->flag) ret.insert(*(p->s));
            }
        }
        return ret;
    }
private:
    Node root;
};

int main() {
    int n;
    cin >> n;
    Trie tree;
    string s;
    for(int i = 0; i < n; i++) {
        cin >> s;
        tree.insert(s);
    }
    tree.build_ac();
    cin >> s;
    auto ans = tree.match(s);
    for(auto x : ans) cout << x << endl;
    cout << "find : " << ans.size() << " item(s) " << endl;
    return 0;
}

#复杂度分析

**对于Trie的匹配来说时间复杂性为:O(max(L(Pi))L(T))其中L串的长度函数,P是模式串,T是目标串。

  对于 AC自动机来说时间复杂性为:O(L(T)+max(L(Pi))+m)气质m是模式串的数量。

  对于 Trie 图 来说时间复杂性为:O(L(T))在此的时间复杂性都是指匹配的复杂度。

  对于构造的代价是 O(sum(L(Pi)))其中sum是求和函数。**

总结

个人习惯把1设为trie的根trie的存储依题而定,或许可以26个指针,有时卡空间的话要用模拟链表如果多组数据要记得清空tot,trie[][],标记数组,最好不要memset建fail。

创作不易 记得三连哦

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python AC自动机是一个用于字符串匹配的算法,它可以高效地在一段文本中查找多个预定义的模式。它的实现可以使用多种库,其中包括ac自动机python和ahocorasick-python。 ac自动机python是一个对标准的ac自动机算法进行了完善和优化的实现,适用于主流的Python发行版,包括Python2和Python3。它提供了更准确的结果,并且可以通过pip进行安装,具体的安装方法可以参考官方文档或者使用pip install命令进行安装。 ahocorasick-python是另一个实现AC自动机的库,它也可以用于Python2和Python3。你可以通过官方网站或者GitHub源码获取更多关于该库的信息和安装指南。 对于AC自动机的使用,一个常见的例子是在一段包含m个字符的文章中查找n个单词出现的次数。要了解AC自动机,需要有关于模式树(字典树)Trie和KMP模式匹配算法的基础知识。AC自动机算法包括三个步骤:构造一棵Trie树,构造失败指针和模式匹配过程。在构造好AC自动机后,可以使用它来快速地在文本中查找预定义的模式,并统计它们的出现次数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ahocorasick-python:AC自动机python的实现,并进行了优化。 主要修复了 查询不准确的问题](https://download.csdn.net/download/weixin_42122986/18825869)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Python实现多模匹配——AC自动机](https://blog.csdn.net/zichen_ziqi/article/details/104246446)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值