首页 > 编程语言 >AC自动机的C++代码实现与过程讲解

AC自动机的C++代码实现与过程讲解

时间:2023-04-21 09:55:50浏览次数:29  
标签:nxt AC val trie C++ int fail 自动机 节点

AC自动机(Aho-Corasick algorithm)是一种多模式字符串匹配算法。它可以快速地查找多个模式串在一段文本串中出现的位置,并支持模式串的预处理,使得在查询时能够快速地匹配。

C++代码实现:

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

const int MAXN = 100005; //最大节点数
const int MAXM = 26; //最大字符集大小

struct Trie_Node //Trie树节点
{
    int id; //节点编号
    int nxt[MAXM]; //子节点
    int fail; //失败指针
    int val; //节点的输出值
}trie[MAXN];

int idx; //Trie树节点计数器

void trie_clear() //清空Trie树
{
    memset(trie, 0, sizeof(trie));
    idx = 0;
}

void trie_insert(char *s, int val) //往Trie树中插入一个字符串
{
    int len = strlen(s);
    int u = 0;
    for(int i = 0; i < len; i++)
    {
        int c = s[i] - 'a';
        if(!trie[u].nxt[c]) //新建节点
            trie[u].nxt[c] = ++idx;
        u = trie[u].nxt[c];
    }
    trie[u].val = val; //该节点的输出值为该字符串的权值
}

void ac_build() //构建AC自动机
{
    queue<int> q;
    for(int c = 0; c < MAXM; c++)
    {
        if(trie[0].nxt[c]) //所有的根节点的子节点的失败指针指向根节点
        {
            trie[trie[0].nxt[c]].fail = 0;
            q.push(trie[0].nxt[c]);
        }
    }
    while(!q.empty()) //BFS求出AC自动机的失败指针
    {
        int u = q.front();
        q.pop();
        for(int c = 0; c < MAXM; c++)
        {
            int v = trie[u].nxt[c];
            if(v)
            {
                trie[v].fail = trie[trie[u].fail].nxt[c]; //与父节点的失败指针相同分两种情况
                if(trie[trie[v].fail].val) //如果该节点的失败指针是可接受节点,那么当前节点就是可接受节点的后缀
                    trie[v].val = trie[trie[v].fail].val; //更新该节点的输出值
                q.push(v);
            }
            else
            {
                trie[u].nxt[c] = trie[trie[u].fail].nxt[c];
            }
        }
    }
}

char s[MAXN]; //输入的文本串
int pos[MAXN]; //所有模式串在文本串中的位置
bool vis[MAXN]; //标记每个出现过的节点
int ans; //输出值的和

void ac_query() //在Trie树上匹配文本串
{
    int u = 0;
    int len = strlen(s);
    for(int i = 0; i < len; i++) //从根节点开始匹配
    {
        int c = s[i] - 'a';
        u = trie[u].nxt[c]; //转移到下一个节点
        int v = u;
        while(v) //利用失败指针更新所有可接受的节点
        {
            if(trie[v].val && !vis[v])
            {
                vis[v] = true;
                ans += trie[v].val;
            }
            v = trie[v].fail;
        }
    }
}

int main()
{
    int n;
    while(cin >> n)
    {
        trie_clear();
        for(int i = 1; i <= n; i++)
        {
            int val;
            cin >> s >> val;
            trie_insert(s, val);
        }
        ac_build();
        cin >> s;
        memset(vis, false, sizeof(vis));
        ans = 0;
        ac_query();
        cout << ans << endl;
    }
    return 0;
}

过程讲解:

  1. 定义Trie树节点
struct Trie_Node //Trie树节点
{
    int id; //节点编号
    int nxt[MAXM]; //子节点
    int fail; //失败指针
    int val; //节点的输出值
};
  1. 往Trie树中插入一个字符串
void trie_insert(char *s, int val) //往Trie树中插入一个字符串
{
    int len = strlen(s);
    int u = 0;
    for(int i = 0; i < len; i++)
    {
        int c = s[i] - 'a';
        if(!trie[u].nxt[c]) //新建节点
            trie[u].nxt[c] = ++idx;
        u = trie[u].nxt[c];
    }
    trie[u].val = val; //该节点的输出值为该字符串的权值
}
  1. 构建AC自动机
void ac_build() //构建AC自动机
{
    queue<int> q;
    for(int c = 0; c < MAXM; c++)
    {
        if(trie[0].nxt[c]) //所有的根节点的子节点的失败指针指向根节点
        {
            trie[trie[0].nxt[c]].fail = 0;
            q.push(trie[0].nxt[c]);
        }
    }
    while(!q.empty()) //BFS求出AC自动机的失败指针
    {
        int u = q.front();
        q.pop();
        for(int c = 0; c < MAXM; c++)
        {
            int v = trie[u].nxt[c];
            if(v)
            {
                trie[v].fail = trie[trie[u].fail].nxt[c]; //与父节点的失败指针相同分两种情况
                if(trie[trie[v].fail].val) //如果该节点的失败指针是可接受节点,那么当前节点就是可接受节点的后缀
                    trie[v].val = trie[trie[v].fail].val; //更新该节点的输出值
                q.push(v);
            }
            else
            {
                trie[u].nxt[c] = trie[trie[u].fail].nxt[c];
            }
        }
    }
}
  1. 在Trie树上匹配文本串
void ac_query() //在Trie树上匹配文本串
{
    int u = 0;
    int len = strlen(s);
    for(int i = 0; i < len; i++) //从根节点开始匹配
    {
        int c = s[i] - 'a';
        u = trie[u].nxt[c]; //转移到下一个节点
        int v = u;
        while(v) //利用失败指针更新所有可接受的节点
        {
            if(trie[v].val && !vis[v])
            {
                vis[v] = true;
                ans += trie[v].val;
            }
            v = trie[v].fail;
        }
    }
}

最后,只需调用以上函数即可。

标签:nxt,AC,val,trie,C++,int,fail,自动机,节点
From: https://www.cnblogs.com/oiercc/p/17339269.html

相关文章

  • 下载Apache软件基金的软件和项目(Hadoop相关组件)
    一、下载Hadoop相关组件,可以到Apache软件基金的资源目录:Apache分发目录地址:https://dlcdn.apache.org/   二、下载软件方法一:在页面中找到需要下载的软件目录,点击进去,选择对应的版本就可以直接下载。方法二:在上面的地址栏中直接加上对应的组件名称,进入后选择对应的版......
  • 如何在 .NET Core WebApi 中处理 MultipartFormDataContent 中的文件
    在上一篇文章(如何在.NETCoreWebApi中处理MultipartFormDataContent)中,我们有描述过如何以最简单的方式在.NETCoreWebApi中处理MultipartFormDataContent。基于框架层面的封装,我们可以快速的从Request.Form中分别拿到文件内容和文本内容,但是这些默认的解析方式都是建......
  • 网络流的C++代码实现与过程讲解
    网络流是一种非常重要的图论算法,它在许多实际问题中得到广泛应用。本文将介绍网络流算法的C++代码实现与过程讲解。算法概述网络流算法是通过将图中的边看作流量通道,将图的点看作流量的起点或终点,来求解图中的最大或最小流量的问题。它是一种非常重要的最优化算法,广泛应用于图论......
  • LogBack 没有打印日志
    背景:某日进行测试,新增了一行日志(项目使用的是logback)报错:无,就是不打印日志解决:经过仔细查看代码,发现之前的人写代码的时候在其它类里面,将privatefinalLoggerlog=LoggerFactory.getLogger(XXXX.class);在Logger工厂中,获取静态绑定的Logger实......
  • Spring的Factories机制介绍
    Java的SPI机制JavaSpringBoot加载yml配置文件中字典项Spring的Factories就是Spring版本的JavaSpi。SpringFactories的最重要的功能就是:可以通过配置文件指定Spring容器加载一些特定的组件。SpringFactories是一种类似于JavaSPI的机制,它在META-INF/spring.factories......
  • GLIBC2.36利用obstack去劫持执行流
    GLIBC2.36中利用obstack去劫持执行流作者没有起名字,可能就是跟houseofapple太相似了,就是roderick师傅提出的houseofapple中没有发现的一个链,个人感觉就是houseofapple跟houseofbanana的一个结合(说实话这两个我已经快忘了怎么用的了所以会将这个攻击封装成几个函数以应......
  • oracle数字类函数
    Oracle数据库中所有的数字类函数:ABS:返回指定数值的绝对值ACOS:返回指定角度的反余弦值ASIN:返回指定角度的反正弦值ATAN:返回指定数字的反正切值ATAN2:返回两个数值的反正切值CEIL:返回大于或等于指定数字的最小整数(向上取整)COS:返回指定角度的余弦值COSH:返回......
  • oracle字符类函数
    Oracle数据库中所有的字符类函数:ASCII:返回某个字符的ASCII码值ASCIISTR:返回字符的ASCII码值的字符串表示CHR:返回指定ASCII码对应的字符CONCAT:连接两个字符串CONVERT:将一个字符集转换成另一个字符集INITCAP:将字符串每个单词首字母大写INSTR:返回字符串中子串的......
  • oracle日期和时间类函数
    Oracle中所有的日期和时间类函数:SYSDATE:返回当前日期和时间CURRENT_DATE:返回当前日期CURRENT_TIMESTAMP:返回当前的日期和时间戳LOCALTIMESTAMP:返回当前时间戳TIMESTAMPADD:在日期上增加一定的数量TIMESTAMPDIFF:计算两个日期之间的时间差EXTRACT:从日期时间......
  • 基于超级电容Supercapacitor和蓄电池的充放电控制系统simulink仿真
    1.算法描述        超级电容器(supercapacitor,ultracapacitor),又叫双电层电容器(ElectricalDoule-LayerCapacitor)、黄金电容、法拉电容,通过极化电解质来储能。它是一种电化学元件,但在其储能的过程并不发生化学反应。这种储能过程是可逆的,也正因为此超级电容器可以反复......