AC自动机

OmniBox2.0.X版本 一站式影视订阅与多端播放平台文档 OmniBox 是一站式影视订阅与多端播放平台:你可以用 JS 或 Python 按 OmniBox 规范自建爬虫源,在后台把多个爬虫源自由组合成订阅,然后在网页、手机、电视等全平台的客户端里使用。只要客户端支持 TVBox、猫源、UZ、Emby 等协议,即可无缝对接。同时支持电视直播源、六大平台直播间和网盘源视频在线观看,通过 Docker 一键部署。 阅读详情

问题:有多个单词,然后给出一个较长的字符串,问有多少个单词出现在这个字符串里面。这里要用到的就是AC自动机,AC自动机需要KMP和字典树知识,最好把这两样都弄懂了。
AC自动机其实就是字典树里面再加一个fail指针,这个fail指针的用处就是当当前字符无法被匹配时所要进行的转移,很想KMP算法里面的next数组,比如对于单词:qwert, wert, ert所构成的字典树如下:(世上最丑作图。。丑出境界了哈哈哈)
这里写图片描述
我自己都要看吐了。。
然后加了fail指针之后:
这里写图片描述
再一次丑出天际。
(PS:其实这里在真正建fail指针的时候是有一点不同的,因为字典树要从a扫到z所以会先扫到e再扫到q再到w,不过其实都是一样的)

还是老套路,结构体:

const int tp = 27;
struct node {
    node *next[tp];  //字符集个数
    node *fail;  // fail指针,AC自动机最重要的东西
    bool ifc;  // 是否是单词结尾
};

然后建立插入单词其实是跟字典树的一样的,这里就不多赘述了,具体可以看一下本人博客里字典树的代码注释:

node *getNewNode() {
    node *p = new node;
    for (int i = 0; i < tp; ++i) p->next[i] = NULL;
    p->fail = NULL;
    p->ifc = false;
    return p;
}

void Insert(char *s) {
    int len = strlen(s);
    node *p = root;
    for (int i = 0; i < len; ++i) {
        int idx = s[i] - 'a';
        if (p->next[idx] == NULL) p->next[idx] = getNewNode();
        p = p->next[idx];
    }
    p->ifc = true;
}

插入完单词以后就是最重要的构建fail指针了,说白了跟KMP的next指针构建原理很像,再看这张丑到爆的图
这里写图片描述
比如说qwert,这个单词里面的e,他的fail指针,以e结尾的子串是qwe,这个子串的后缀为we 和 e,fail指针指向的就是某个以we或者e为前缀的单词里面的那个e(说的我自己都要晕了),如果不存在的话就指向root,还有root的儿子节点的fail指针都指向root。
看图 qwe中we是qwe的后缀,也是wert的前缀,所以e指向wert的e,就是这么个情况。建立fail指针的时候用到的是BFS,需要一层一层的建立,因为子节点的fail指针需要父节点的fail指针。

struct node *root;
struct node *que[maxn]; // 队列
void buildACnode() {
    int l = 0, r = 1;
    que[0] = root;
    while (l < r) {  // 用数组模拟队列
        node *tmp = que[l++];
        if (tmp == root) { // root儿子节点的fail指向root
            for (int i = 0; i < tp; ++i)
                if (tmp->next[i]) {
                    tmp->next[i]->fail = root;
                    que[r++] = tmp->next[i]; // 别忘了加入队列中
                }
        } else {
            for (int i = 0; i < tp; ++i) {
                if (tmp->next[i]) {
                    node *p = tmp->fail;
                    while (p && !p->next[i]) p = p->fail;//看父节点的fail指针指向的节点是否有i这个节点,直到p为空或者找到符合的节点
                    if (!p) tmp->next[i]->fail = root;//没有符合的节点,指向root
                    else tmp->next[i]->fail = p->next[i];
                    que[r++] = tmp->next[i];
                }
            }
        }
    }
}

最后就剩下查询了:
查询其实就是顺着字典树往下走,如果匹配这个节点那么接着往下走,否则跳转到这个节点的fail指针继续寻找是否有匹配节点

int query(char *str) {
    int cnt = 0, len = strlen(str);
    node *p = root;
    for (int i = 0; i < len; ++i) {
        int id = str[i] - 'a';
        while (!p->next[id] && p != root) p = p->fail; //若当前没有能够匹配的节点则跳转到该节点的fail指针继续检测
        p = p->next[id];
        if (!p) p = root;  // 遍历完所有fail指针都没有那么返回到root,已经不可能继续匹配这个单词了
        node *t = p;
        while (t != root) {  // 遍历这个节点的所有直接间接相连的fail指针,看有没有某个节点是单词结束字符
            if (t->ifc) {
                cnt++;
//                t->ifc = false;
            }// else break;
            t = t->fail;
        }
    }
    return cnt;
}

然后就大功告成了!

总代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;

const int tp = 27;
const int maxn = 100000 + 7;

struct node {
    node *next[tp];
    node *fail;
    bool ifc;
};

struct node *root;
struct node *que[maxn];

node *getNewNode() {
    node *p = new node;
    for (int i = 0; i < tp; ++i) p->next[i] = NULL;
    p->fail = NULL;
    p->ifc = false;
    return p;
}

void Insert(char *s) {
    int len = strlen(s);
    node *p = root;
    for (int i = 0; i < len; ++i) {
        int idx = s[i] - 'a';
        if (p->next[idx] == NULL) p->next[idx] = getNewNode();
        p = p->next[idx];
    }
    p->ifc = true;
}

void buildACnode() {
    int l = 0, r = 1;
    que[0] = root;
    while (l < r) {
        node *tmp = que[l++];
        if (tmp == root) {
            for (int i = 0; i < tp; ++i)
                if (tmp->next[i]) {
                    tmp->next[i]->fail = root;
                    que[r++] = tmp->next[i];
                }
        } else {
            for (int i = 0; i < tp; ++i) {
                if (tmp->next[i]) {
                    node *p = tmp->fail;
                    while (p && !p->next[i]) p = p->fail;
                    if (!p) tmp->next[i]->fail = root;
                    else tmp->next[i]->fail = p->next[i];
                    que[r++] = tmp->next[i];
                }
            }
        }
    }
}

int query(char *str) {
    int cnt = 0, len = strlen(str);
    node *p = root;
    for (int i = 0; i < len; ++i) {
        int id = str[i] - 'a';
        while (!p->next[id] && p != root) p = p->fail;
        p = p->next[id];
        if (!p) p = root;
        node *t = p;
        while (t != root) {
            if (t->ifc) {
                cnt++;
//                t->ifc = false;
            }// else break;
            t = t->fail;
        }
    }
    return cnt;
}

char s[1000];

int main() {
    root = getNewNode();
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%s", s);
        Insert(s);
    }
    buildACnode();
    scanf("%s", s);
    int res = query(s);
    printf("answer:%d\n", res);
    return 0;
}

/**
3
qwert
wert
ert
qwerty
**/
【DEIM创新改进】全网独家创新、注意力改进篇 | AAAI 2026 | DEIM 引入轻量级群体注意力(LWGA)模块,可以显著提高其多尺度物体检测能力,特别是在小物体和复杂背景下的表现 本文提出了一种改进的DEIM网络模型,通过引入轻量级LWGA模块显著提升了多尺度物体检测性能。LWGA模块采用局部和全局注意力机制,有效提升了小物体和复杂背景下的识别能力,同时保持低计算成本。该模块通过分割输入特征图并应用多种注意力机制(GPA、RLA、SMA、SGA)进行特征提取和融合,优化了多尺度物体的特征表示。实验表明,改进后的DEIM模型在遥感图像处理等任务中表现出色,在12个数据集上实现了性能与计算效率的最佳平衡,为资源受限设备提供了高效的解决方案。 阅读详情

相关推荐

强化学习环境Gym库的介绍

Gym是一个用于进行强化学习实验的开源库,它提供了一个仿真环境,内置了多种仿真游戏,例如出租车游戏、悬崖游戏等,不同的游戏所用的网格、规则、奖励(reward)都不一样,适合为强化学习做测试。(1) 多种环境:Gym库内置了上百种实验环境,包括算法环境、简单文本环境、经典控制环境、Atari游戏环境、MuJoCo环境和机械控制环境等。(2) Gym提供多种环境供我们使用,这里以一个简单的CartPole-v0为例,首先导入gym库,然后创建一个CartPole-v0环境,通过调用gym.make方法实现。

ahah12345678的博客 2147

AC自动树

上期我们学习了Trie 字典树(),本期我们将学习AC自动树。

xuxianshun666的博客 584

Win11笔记本BIOS设置避坑大全:Secure Boot、RST、UEFI引导,一次讲清

本文详细解析了Win11笔记本BIOS设置中的关键问题,包括Secure Boot、Intel RST和UEFI引导的配置方法,帮助用户避开双系统安装中的常见陷阱。特别针对不同品牌笔记本的BIOS界面差异,提供了实用的操作指南和优化建议,确保系统兼容性和启动效率。

weixin_29251131的博客 686

trie 模板

const int maxn = 1e6+10; int tr[maxn][30]; //第二维数组大小视题目要求而定 int sum[maxn]; //统计前缀 int tot,root; char s[maxn]; //bool vis[30*maxn]; //标记节点是否为字符串结尾 //int ans; void insert() { int len = strle...

没有梦想何以远行 144

飞牛NAS+Docker打造私人IPTV直播源:手把手教你部署iptv-api

本文详细介绍了如何在飞牛NAS上通过Docker部署iptv-api,打造私人IPTV直播源。从SSH配置到Docker容器部署,手把手教你实现自动更新直播源、多终端支持的解决方案,适用于Kodi、PotPlayer等播放器,提升家庭娱乐体验。

earth的博客 5560

华硕电脑Win10改Win7系统:BIOS设置详解

通过上述步骤,华硕电脑用户可以将系统从Windows 10更改为Windows 7。虽然这个过程可能涉及一些技术细节和潜在的风险(如数据丢失和驱动不兼容),但只要按照指南仔细操作并做好准备工作,大多数用户应该能够成功完成系统回滚。请记住,在进行任何系统更改之前都要备份重要数据,并确保已了解所有潜在的风险和限制。希望本文能够帮助到需要回滚到Windows 7的华硕电脑用户!

nntxthml的博客 2261

Trie树和AC自动机的概念

Trie树 Trie树:也叫“字典树”,专门处理字符串匹配的数据结构,用来在一组字符串集合中快速查找某个字符串 Trie树的本质:利用字符串之间的公共前缀,将重复的前缀合并在一起 Trie树主要有两个操作:①将字符串集合构造成Trie树②在Trie树中查询一个字符串 Trie树非常耗内存,用的是空间换时间:Trie树实现的时候,用数组来存储一个节点的子节点的指针。如果字符串中包含从a到z这 26个字符,那每个节点都要存储一个长度为26的数组,并且每个数组元素要存储一个8字节指针(或者是4字节)。而且,即便

frostmelody 全网同名,大家多多关注呀~ 持续分享优质内容! 443

手把手教你用CODESYS将旧笔记本改造成软PLC(含证书配置避坑指南)

本文提供了一份详尽的指南,教你如何利用CODESYS平台将闲置旧笔记本改造为一台功能完整的软PLC。内容涵盖从硬件评估、系统实时性优化、软件安装与驱动配置,到关键的证书授权管理避坑指南,旨在帮助自动化工程师和爱好者低成本搭建稳定可靠的学习与开发平台。

weixin_29190573的博客 278

AC自动机-详解AC自动机以及模板

AC自动机-详解AC自动机以及模板AC自动机算法简介AC自动机算法大致流程AC自动机详细图解AC自动机模板题与模板题目内容代码详解完整代码 AC自动机算法简介 首先简要介绍一下AC自动机,英文名:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模板匹配算法之一。一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。 要搞懂AC自动机,先要有字典树Trie和KMP模式匹配算法的基础知识。其中,KMP是用于一对一的字符串匹

qq_43381135的博客 6854

PID详解

1、什么是PID及其作用 设定一个输出目标,反馈系统传回输出值,如与目标不一致,则存在一个误差,PID 根据此误差调整输入值,直至输出达到设定值. 为什么需要PID呢,比如控制温度,我们需要设置一个要达到的目标温度值,当将温度稳定在目标温度值 例如,我们希望水的目标温度值为70°,其希望无非就是可以快速而且无抖动的达到70°,并稳定在70° 如果我们使用普通的方式加热,当温度到达70°后就以及停止加热,岂不是简单快速,当然用在要求不高的场合也可以,但是如果要求更高的场合呢,会出现温度到达70°后......

weixin_48435215的博客 5万+

安全与加密常识(10)linux系统证书管理

1. 将自定义CA证书(如my-ca.crt)放到源目录# 2. 编辑配置文件,启用该证书(末尾添加一行 my-ca.crt)# 3. 执行命令同步(核心工作环节)# 4. 验证结果:检查是否生成哈希链接和合并到证书束ls /etc/ssl/certs/my-ca.pem # 证书文件ls /etc/ssl/certs/$(openssl x509 -hash -noout -in /usr/share/ca-certificates/my-ca.crt).0 # 哈希链接。

二进制君 282

AC自动机(详解)

需要提前了解的知识:KMP, 字典树 例题:传送门 AC自动机是KMP和字典树的结合体, 如果说KMP用于单模式匹配,那么AC自动机是用于多模式匹配的。 举个例子,KMP适用于在一篇文章中找一句话, AC自动机适合在一篇文章中寻找多句话(并不是简单的多用几次KMP)。 KMP算法构建了一个查询表(nex数组), 我们可以按照相同的思路构建nex表,在ac自动机中成为失配数组。 算法思路: 先将所有的模式串建立字典树, 建立nex数组,然后就开始匹配。 ...

三季人的博客 1万+

[DDR5] 模式寄存器 Mode Register MRR/MRW

模式寄存器用于定义各种操作模式。在初始化过程中,可以通过重新执行MRS命令来更改模式寄存器的内容。

串起点滴 1634

字符串处理【AC自动机】 - 原理 AC自动机详解

字符串处理【AC自动机】 - 原理 AC自动机详解

代码改变世界 3589

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

AC自动机详细讲解

AC自动机详细讲解

温姑娘的博客 2699

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

AC自动机算法

AC自动机简介:  首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一。一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。要搞懂AC自动机,先得有字典树Trie和KMP模式匹配算法的基础知识。KMP算法是单模式串的字符匹配算法,AC自动机是多模式串的字符匹配算法。

飘过的小牛 9万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

AC自动机总结

如果你还不知道KMP是什么,或者不知道Trie是什么,先点下面的链接,学好这两样再来看这篇文章。 KMP入门教程传送门。 Trie入门教程传送门。 好,那么我来讲讲AC自动机吧。 以洛谷P3808模板题为例, 如果我们有n个模式串,和一个文本串,我们需要统计有多少个模式串在文本串中出现了,怎么办? 显然,如果我们直接枚举每个模式串,然后将其与文本串进行匹配,判断是否匹配成功,那么效率就

Holylight_Knight 1641
上一篇: KMP算法
下一篇: AVL树(平衡二叉树)
THE___BEST
博客等级 码龄12年 10粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值