尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

AC自动机(拓扑排序优化)

AC自动机(拓扑排序优化)
📅 发布时间:2026/6/20 6:47:29
/*
本质:Trie的基础上+KMP的思想(失配指针)
匹配过程中,为了防止失败就前功尽弃,不能利用之前匹配结果的情况,就发明了KMP
如果将KMP运用到高效的Trie上,就可以实现多模式串的匹配
每次匹配失败,就去查找具有最长相同前缀的链
例如,对于abchi和chj这两条链,当我们遍历至i,我们发现父节点的失配指针指向chj中的h,于是我们想知道,chj中h的子节点是否为i,不是,我们跳转过来
继续查找有没有相同的,直至找到或找到空结点
但是,这样的时间复杂度肯定很高,所以我们在处理空结点时,如chj(在代码实现)中h后有一个为空的i节点,那么我们直接i将应该跳转至的节点(即失配指针)
储存下来,这样就可以一步到位(因为是BFS按层序遍历,先处理前面的,正确性可以保证),不用多次跳转了“每个结点的fail指针表示由         根结点到该结点所组成的字符序列的所有后缀        和 整个目标字符串集合(也就是整个Trie树)中的      所有前缀   两者中最长公共的部分。”
*/
#include <iostream>
#include <cstring>
#include <queue>using namespace std;const int N = 2e5+5;
struct node {int son[26];int idx;int deg;//拓扑优化int ans;int fail;void init() {memset(son, 0, sizeof son);deg = idx = 0;}
};
node trie[N];
int pidx, tot;
int ans[N], idx[N];
void insert(string s, int& idx) {int u = 0;for (int i = 0; i < int(s.size()); i++) {int c = s[i] - 'a';if (!trie[u].son[c])trie[u].son[c] = ++tot;u = trie[u].son[c];}// 由于有可能出现相同的模式串,需要将相同的映射到同一个编号if (!trie[u].idx)trie[u].idx = ++pidx;idx = trie[u].idx;
}
void BFS() {/*每个结点fail指向的解决顺序是按照广度优先遍历的顺序完成的,或者说层序遍历的顺序进行的,也就是说我们是在解决当前结点的孩子结点fail的指向时,当前结点的fail指针一定已指向了正确的位置。*/queue<int> q;for (int i = 0; i < 26; i++)if (trie[0].son[i])q.push(trie[0].son[i]);while (!q.empty()) {int u = q.front();q.pop();for (int i = 0; i < 26; i++) {if (trie[u].son[i]) {trie[trie[u].son[i]].fail = trie[trie[u].fail].son[i];//父节点的失配指针已经求出来了,那么子节点的失配指针就是去寻找父节点失配指针,看一下有无对应子节点(为什么只需要找一次,接着看)trie[trie[trie[u].fail].son[i]].deg++;//增加度数q.push(trie[u].son[i]);}elsetrie[u].son[i] = trie[trie[u].fail].son[i];//这就是为什么只用寻找一次的原因,本来子节点的失配指针应该一直跳转着寻找,如果跳转的地方为空,就接着跳,但是通过这个优化,提前处理好,就不需要额外跳了}}/*所有的fail边会构成一棵树,因为fail显然不成环(显然,abcedfg...不可能是a的前缀),而且只会从深处指向浅处(否则前缀必定不同)*//*观察到时间主要浪费在在每次都要跳 fail。如果我们可以预先记录,最后一并求和,那么效率就会优化。于是我们按照 fail 树,做一次内向树上的拓扑排序,就能一次性求出所有模式串的出现次数。build 函数在原先的基础上,增加了入度统计一部分,为拓扑排序做准备。*/
}
void query(string tmp) {int u = 0;for (int i = 0; i < int(tmp.size()); i++) {u = trie[u].son[tmp[i] - 'a'];trie[u].ans++;//统计}
}
void toposort(){//普通的拓扑排序queue<int> q;for (int i = 0; i <= tot; i++)if (!trie[i].deg) q.push(i);while (!q.empty()) {int u = q.front();q.pop();ans[trie[u].idx] = trie[u].ans;int v = trie[u].fail;trie[v].ans += trie[u].ans;if (!--trie[v].deg) q.push(v);}
}int main() {cin.tie(nullptr) -> sync_with_stdio(false);int n;cin >> n;for (int i = 1; i <= n; i++) {string s;cin >> s;insert(s, idx[i]);}BFS();string s;cin >> s;query(s);toposort();for (int i = 1; i <= n; i++) cout << ans[idx[i]] << '\n';
}

相关新闻

  • moji 辞书 注音分析
  • 实用指南:OSPF LSA Type 3(Summary LSA)概念及题目
  • .net解决分布式事务简单方案DotNetCore.CAP

最新新闻

  • Qwen3 MOE架构与Reasoning RL技术解析及本地部署实战
  • BIND DNSSEC实战配置:从密钥生成到ad标志验证
  • 基于LLM嵌入与SVM的临床文本特征工程:创伤后癫痫预测实践
  • 2026枣庄漏水检测维修本地口碑防水商家榜单:厨卫/阳台/屋面/地下室渗漏水维修,持证施工+明码实价,防水补漏公司TOP5推荐 - 即刻修防水
  • Vue v-for原理深度解析:从数据驱动到虚拟DOM复用
  • GPT-4o替代Gemini的生产力迁移实战:上下文稳定性与提示词工程

日新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

周新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号