AC自动机

AC自动机

AC自动机有一个很出色的功能:实现多模式匹配。

多模式匹配:模式串有多个,主串只有一个,要进行多次模式串匹配。如果用KMP就要一个一个模式串进行匹配,效率低。AC自动机就可以做到,只要经过一些预处理之后,扫描一遍主串,就可以找出所有模式串。

fail指针含义:若fail[i]=j,则word[j]word[i]的最长后缀。
fail指针的目的和意义:通过fail指针,把以s[i]为结尾的所有后缀的个数加起来,从而把匹配的模式串加起来。
如何构建fail指针:bfs层次遍历构建。
这个AC自动机感觉也可以 查后缀(想想fail指针的含义)

在这里插入图片描述

数据结构:

int n; //模式串个数
string s; //模式串
string text; //文本串
int trie[1000006][30],cnt[1000006],idx,fail[1000006]; //重要数据结构

先将输入的模式串构建成一棵字典树

void insert(string s)
{int p=0;for(int i=0;s[i];++i){int c=s[i]-'a';if(!trie[p][c]) trie[p][c]=++idx;p=trie[p][c];}cnt[p]++;
}

再设置各个节点的fail指针:bfs(层次遍历)

void getfail()
{queue<int> q;for(int i=0;i<26;++i){if(trie[0][i])q.push(trie[0][i]);fail[trie[0][i]]=0;}while(q.size()){int now=q.front();q.pop();for(int i=0;i<26;++i){if(trie[now][i]){fail[trie[now][i]]=trie[fail[now]][i];q.push(trie[now][i]);}elsetrie[now][i]=trie[fail[now]][i];}}
}

遍历文本串,查询出有多少个匹配的模式串:

int query(string s)
{int now=0,ans=0;for(int i=0;s[i];++i){now=trie[now][s[i]-'a'];for(int j=now;j&&cnt[j]!=-1;j=fail[j]){ans+=cnt[j];cnt[j]=-1;}}return ans;
}

分享B站学习链接:

1.[算法]轻松掌握ac自动机_哔哩哔哩_bilibili

刷题练手链接:

P3808 【模板】AC 自动机(简单版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

参考代码:

#include <bits/stdc++.h>
using namespace std;int n;
string s;
string text;
int trie[1000006][30],cnt[1000006],idx,fail[1000006];
//vector<int> v[N]; //可以存编号为i的结点存放了字符串长度为多少的串 void insert(string s)
{int p=0;for(int i=0;s[i];++i){int c=s[i]-'a';if(!trie[p][c]) trie[p][c]=++idx;p=trie[p][c];}cnt[p]++;
}void getfail()
{queue<int> q;for(int i=0;i<26;++i){if(trie[0][i])q.push(trie[0][i]);fail[trie[0][i]]=0;}while(q.size()){int now=q.front();q.pop();for(int i=0;i<26;++i){if(trie[now][i]){fail[trie[now][i]]=trie[fail[now]][i];q.push(trie[now][i]);}elsetrie[now][i]=trie[fail[now]][i]; //在构建fail指针时,trie在改变,它记录着 在上层 最近的 哪里 会有i这个元素 }}
}int query(string s)
{int now=0,ans=0;for(int i=0;s[i];++i){
//		cout<<i<<' '<<s[i]<<' '<<now<<' '<<trie[now][s[i]-'a']<<endl;now=trie[now][s[i]-'a'];for(int j=now;j&&cnt[j]!=-1;j=fail[j]){
//			cout<<j<<' '<<fail[j]<<endl; ans+=cnt[j];cnt[j]=-1; //这个模板有个问题:只能做一次询问。}}return ans;
}int main()
{cin >> n;while(n--){cin >> s;insert(s);}cin >> text;getfail();cout << query(text) << endl;return 0;
}

模板注意点:
1.插入的字符可以是什么?一般是’a’-'z’共26种.但也有题目会说是 可见字符,可见字符 应该算95个,从32-126
2.如果有多个主串,要注意加一个bool flag[N]处理一下。

裸题:HDU-2222 Keywords Search

2017 ICPC 青岛网络赛 C-The Dominator of Strings
题意:多组样例,每次给出N个字符串,求出N个串的母串。
代码:一般参考以下模板!

#include <bits/stdc++.h>
using namespace std;#define fi first
#define se second
int T,n,len;
string s,text;
int trie[100006][26],idx,fail[100006],cnt[100006];void Init() {for(int i=0;i<=idx;i++){cnt[i]=0;
//        fail[i]=0;  for(int j=0;j<26;j++)trie[i][j]=0;}idx=0;
}inline void insert(string s){int p=0;for(int i=0;s[i];++i){int c=s[i]-'a';if(!trie[p][c]) trie[p][c]=++idx;p=trie[p][c];}cnt[p]++;
}/*
感觉以下做法:"有一个位置匹配了,就往后配" 这种做法 比较暴力
但有些题 竟然 跑得更快   很奇怪很奇怪很奇怪。。。 
*/ 
int query(string s){int ans=0;for(int i=0;s[i];++i){int c=s[i]-'a';int u=0;int j=0;while(trie[u][c]){if(cnt[trie[u][c]]){ans+=cnt[trie[u][c]];cnt[trie[u][c]]=0;}u=trie[u][c];j++;if(i+j>=s.size()) break; //注意加这一行!不然有些时候会寄! c=s[i+j]-'a';}}return ans;
}/*
正常的AC自动机 模板 
*/
//void getfail(){
//	queue<int> q;
//	for(int i=0;i<26;++i) {
//		if(trie[0][i]) q.push(trie[0][i]);
//		fail[trie[0][i]]=0;
//	}
//	while(q.size()){
//		int now=q.front();
//		q.pop();
//		for(int i=0;i<26;++i){
//			if(trie[now][i]){
//				fail[trie[now][i]]=trie[fail[now]][i];
//				q.push(trie[now][i]);
//			}else{
//				trie[now][i]=trie[fail[now]][i];
//			}
//		}
//	}
//}
//
//int query(string &s){
//	int now=0,ans=0;
//	for(int i=0;s[i];++i){
//		now=trie[now][s[i]-'a'];
//		for(int j=now;j&&cnt[j]!=-1;j=fail[j]){
//			ans+=cnt[j];
//			cnt[j]=-1;
//		}
//	}
//	return ans;
//}int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>T;while(T--){cin>>n;Init(); //学习! len=0;for(int i=1;i<=n;++i) {cin>>s;insert(s);if(s.size()>len) text=s,len=s.size();}
//		getfail();if(query(text)==n) cout<<text<<'\n';else cout<<"No\n";}
}

例题:HDU - 2896 病毒侵袭
参考代码:
这道题 字符是可见字符,有多个主串

#include <bits/stdc++.h>
using namespace std;int n,m;
string s,text;
vector<int> v;
int tot;const int N=1e6+5;
int trie[N][100],cnt[N],idx,fail[N];
bool flag[N]; //标记 void insert(string s,int id){int p=0;for(int i=0;s[i];++i){int c=s[i]-32;if(!trie[p][c]) trie[p][c]=++idx;p=trie[p][c];}cnt[p]=id;
}void getfail(){queue<int> q;for(int i=0;i<95;++i){if(trie[0][i])q.push(trie[0][i]);fail[trie[0][i]]=0;}while(q.size()) {int now=q.front();q.pop();for(int i=0;i<95;++i){if(trie[now][i]){fail[trie[now][i]]=trie[fail[now]][i];q.push(trie[now][i]);}else trie[now][i]=trie[fail[now]][i];}}
}void query(string s){int now=0;for(int i=0;s[i];++i){now=trie[now][s[i]-32];for(int j=now;j&&cnt[j]!=-1;j=fail[j]){if(cnt[j] && flag[j]) v.push_back(cnt[j]);flag[j]=0; //这个有个问题:只能查询一次 }}
}int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;for(int i=1;i<=n;++i){cin>>s;insert(s,i);}getfail();cin>>m;for(int i=1;i<=m;++i){cin>>text;v.clear();for(int i=0;i<=idx;++i) if(cnt[i]) flag[i]=1; //为了实现多次扫描主串 query(text);if(v.size()){sort(v.begin(),v.end());tot++;cout<<"web "<<i<<":";for(auto x:v) cout<<' '<<x;cout<<'\n';}}cout<<"total: "<<tot<<'\n';
}

HDU - 3065 病毒侵袭持续中
这道题 主串有多个子串会跟模式串匹配要算多次
怎么办?答:去掉cnt[j]=-1 以及 条件的 cnt[i]!=-1
这样,主串出现多个模式串,就能算多次了 。
参考代码:

#include <bits/stdc++.h>
using namespace std;int n;#define pii pair<int,int>
#define fi first
#define se second
#define ms(a,x) memset(a,x,sizeof a)
//数据结构
const int N=5e4+10; //应该是模式串总长 
string s[1003],text;
int trie[N][100],cnt[N],idx,fail[N];
vector<pii> v;
unordered_map<int,int> mp; void insert(string s,int k){int p=0;for(int i=0;s[i];++i){int c=s[i]-'A';if(!trie[p][c]) trie[p][c]=++idx;p=trie[p][c]; }cnt[p]=k;
}void getfail(){queue<int> q;for(int i=0;i<26;++i){if(trie[0][i])q.push(trie[0][i]);fail[trie[0][i]]=0;}while(q.size()){int now=q.front();q.pop();for(int i=0;i<26;++i){if(trie[now][i]){fail[trie[now][i]]=trie[fail[now]][i];q.push(trie[now][i]);}else trie[now][i]=trie[fail[now]][i];}}
}void query(string s){int now=0;for(int i=0;s[i];++i){if(s[i]>'Z' || s[i]<'A') {now=0;continue;}now=trie[now][s[i]-'A'];for(int j=now;j;j=fail[j]){if(cnt[j]) mp[cnt[j]]++;}}
}bool cmp(pii x,pii y){return x.fi<y.fi;
}int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);while(cin>>n){ms(trie,0);ms(cnt,0);ms(fail,0);idx=0;v.clear();mp.clear();for(int i=1;i<=n;++i){cin>>s[i];insert(s[i],i);}getfail();cin>>text;query(text);for(auto x:mp) v.push_back(x); sort(v.begin(),v.end(),cmp);for(auto x:v){cout<<s[x.fi]<<": "<<x.se<<'\n';}}
} 
由若干模式串构建串

HDU - 2825 Wireless Password
题意:给你M个模式串,问有多少种方案 构造长度为N的 至少包含K个模式串的 串。
注意:
1.字符串会包含,如有模式串she he,串she包含由模式串she和he
有一点 串包含 的意思在里面,所以可以想到用AC自动机把模式串全部扔进去。
2.然后用状压dp进行转移

参考

代码:

#include <bits/stdc++.h>
using namespace std;const int N=110;
const int MOD=20090717;
int trie[N][26],fail[N],cnt[N],idx;
int n,m,k;
string s;
int dp[30][110][1<<10]; //dp[i][j][k]:走到第i步 到达j结点 状态为k 的方案数 
int num[1<<10]; //num[i]:状态为i的1的数目 void insert(string s,int id){int p=0;for(int i=0;s[i];++i){int c=s[i]-'a';if(!trie[p][c]) trie[p][c]=++idx;p=trie[p][c];}cnt[p]=1<<id;
}void getfail(){queue<int> q;for(int i=0;i<26;++i){if(trie[0][i])q.push(trie[0][i]);fail[trie[0][i]]=0;}while(q.size()){int now=q.front();q.pop();cnt[now]|=cnt[fail[now]]; //因为提到 字符串 后缀包含 for(int i=0;i<26;++i){if(trie[now][i]){fail[trie[now][i]]=trie[fail[now]][i];q.push(trie[now][i]);}elsetrie[now][i]=trie[fail[now]][i];}}
}void getsum(){for(int i=0;i<(1<<10);++i){num[i]=0;for(int j=0;j<10;++j)if(i&(1<<j)) num[i]++;}
} int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);getsum(); //预处理 while(cin>>n>>m>>k && (n||m||k)){memset(trie,0,sizeof trie);memset(fail,0,sizeof fail);memset(cnt,0,sizeof cnt);idx=0; memset(dp,0,sizeof dp);for(int i=1;i<=m;++i) {cin>>s;insert(s,i-1);}getfail();dp[0][0][0]=1; for(int i=0;i<n;++i){ //遍历字符 for(int j=0;j<=idx;++j){ //遍历结点编号 for(int kk=0;kk<(1<<m);++kk){ //遍历状态 if(dp[i][j][kk]){for(int id=0;id<26;++id){ //遍历j号点下面的各个分支点 int u=trie[j][id];int state=kk|cnt[u];dp[i+1][u][state]+=dp[i][j][kk];dp[i+1][u][state]%=MOD;}} }}} int ans=0;for(int i=0;i<(1<<m);++i){
//			cout<<num[i]<<endl;if(num[i]>=k){for(int j=0;j<=idx;++j){ans=(ans+dp[n][j][i])%MOD;}}}cout<<ans<<endl;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/6393.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【全网首出】npm run serve报错 Expression: thread_id_key != 0x7777

总结 困扰了一天&#xff01;&#xff01;&#xff01;一直以为是自己哪里配置错了&#xff0c; 结果最后发现是node.js官方的问题&#xff0c; Node.js v16.x版本的fibers.node被弃用 本文阅读大概&#xff1a;3min #npm run serve时就报错 #找了一天的文章&#xff0c;找不…

# 从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;八&#xff09;Sentinel&#xff08;1&#xff09; 一、sentinel&#xff1a;概述 1、前言 – 服务熔断 Hystrix 的替换方案。 1&#xff09;2018年底 Netflix 官方宣布 Hystrix 已经足够稳定&#xff0c;不再积极开发 Hys…

JVM笔记2--垃圾收集算法

1、如何确认哪些对象“已死” 在上一篇文章中介绍到Java内存运行时的各个区域。其中程序计数器、虚拟机栈、本地方法栈3个区域随着线程而生&#xff0c;随线程而灭&#xff0c;栈中的栈帧随着方法的进入和退出而有条不紊的执行着入栈和出栈操作。每个栈帧中分配多少内存基本上…

组队竞赛和删除公共字符

这里附上两个题目的链接 题目一&#xff1a;删除公共字符_牛客题霸_牛客网 (nowcoder.com) 题目二&#xff1a;组队竞赛_牛客笔试题_牛客网 (nowcoder.com) 第一题 分析&#xff1a; 当我们看见这个题目的时候&#xff0c;可以使用传统的暴力查找方式&#xff0c;如判断第一个…

VsCode | 修改首页启动页 Logo

VsCode | 修改首页启动页 Logo 最终效果&#xff1a; 插件的安装 先安装插件 Custom CSS and JS Loader 插件配置 Ctrl Shift P 输入 打开用户设置&#xff0c;在末尾添加 "vscode_custom_css.imports": [""]下载 Logo 下载 Logo 点我下载 引入…

亚马逊云科技AWS免费证书-EC2服务器设计(含题库)

亚马逊云AWS官方程序员专属免费证书又来了&#xff01;这次证书是关于AWS EC2实例的设计和搭建&#xff0c;EC2作为AWS服务的核心&#xff0c;是学好AWS的第一步。强推没有任何AWS背景和转码的小伙伴去学&#xff01;学完也能变成AWS开发大神&#xff01; 证书名字叫Getting St…

使用 TensorFlow 和 Keras 构建 U-Net

原文地址&#xff1a;building-a-u-net-with-tensorflow-and-keras 2024 年 4 月 11 日 计算机视觉有几个子学科&#xff0c;图像分割就是其中之一。如果您要分割图像&#xff0c;则需要在像素级别决定图像中可见的内容&#xff08;执行分类时&#xff09;&#xff0c;或者从像…

Oracle23ai来了,23爱,全能、超级巨兽...

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、My…

[图解]关于SysML v2(1)大刀阔斧 对比 伪创新圈子

1 00:00:03,960 --> 00:00:08,270 OMG在2月份&#xff0c;这里写了4月 2 00:00:08,440 --> 00:00:13,530 应该是2月&#xff0c;发布了 3 00:00:13,870 --> 00:00:17,700 SysML v2的 beta 2版本 4 00:00:17,870 --> 00:00:19,780 也是当前最新的版本 5 00:00:2…

ES的脑裂现象

目录 0 集群结点的职责1 什么是脑裂现象2 造成脑裂现象的原因2.1 网络问题&#xff08;最常见&#xff09;2.2 主节点负载过大&#xff0c;资源耗尽&#xff0c;别的结点ping不到主节点2.3 主节点JVM内存回收时间过长导致 3 脑裂现象的解决方案3.1 局域网部署3.2 角色分离&…

python实验一 简单的递归应用

实验一 实验题目 1、兔子繁殖问题(Fibonacci’s Rabbits)。一对兔子从出生后第三个月开始&#xff0c;每月生一对小兔子。小兔子到第三个月又开始生下一代小兔子。假若兔子只生不死&#xff0c;一月份抱来一对刚出生的小兔子&#xff0c;问一年中每个月各有多少只兔子。 &…

[每日AI·0501]GitHub 版 Devin,Transformer的强力挑战者 Mamba,Sora 制作细节与踩坑,OpenAI 记忆功能

AI 资讯 国资委&#xff1a;加快人工智能等新技术与制造全过程、全要素深度融合GitHub版 Devin 上线&#xff0c;会打字就能开发应用&#xff0c;微软 CEO&#xff1a;重新定义 IDE在12个视频理解任务中&#xff0c;Mamba 先打败了 TransformerSora 会颠覆电影制作吗&#xff…

Oracle 23c? No Oracle 23ai

昨天 Oracle 发布了最新的Oracle版本。出乎意料的是这个版本从Oracle 23c 更名为 Oracle 23ai &#xff0c;似乎预示着Oracle的掌舵人Larry也要全面拥抱AI技术浪潮了。 23ai版本主要功能介绍: Oracle Database 23ai 是 Oracle 数据库的下一个长期支持版本。它包括 300 多项新功…

【LeetCode刷题】410. 分割数组的最大值

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 410. 分割数组的最大值 2. 题目描述 3. 解题方法 题目中提到的是某个和的最大值是最小的&#xff0c;这种题目是可以用二分来解决的。 确定区间&#xff0c;根据题目的数据范围&#xff0c;可以确定区间就是[0, 1e9]…

LEETCODE LCR 041. 数据流中的移动平均值

class MovingAverage:def __init__(self, size: int):"""Initialize your data structure here."""self.sizesize1self.front0self.rear0self.queue[None for _ in range(size1)]self.sum0def next(self, val: int) -> float:# 满了if (self.…

postman中百度preview无法加载的解决方案

问题 在使用postman关联时&#xff0c;百度接口与天气接口已使用glb_city关联&#xff0c;但在百度接口发送请求时&#xff0c;发现preview无法加载 解决方案 1、进入百度 百度全球领先的中文搜索引擎、致力于让网民更便捷地获取信息&#xff0c;找到所求。百度超过千亿的中…

LeetCode面试298,二叉树最长连续序列(Python)

开始想着dfs&#xff0c;两种情况 1.以root为根 2.不以root为根 但是这样需要两个dfs分别进行&#xff0c;那么时间复杂度就上去了。 class Solution:def longestConsecutive(self, root: Optional[TreeNode]) -> int:def dfs(root):# 以root为根节点&#xff0c;可以延…

leetCode68. 文本左右对齐

基本思路&#xff1a; leetCode68. 文本左右对齐 代码 class Solution { public:vector<string> fullJustify(vector<string>& words, int maxWidth) {vector<string> res;for(int i 0; i < words.size(); i){ // 枚举有多少个单词int j i 1; //…

MATLAB中自定义栅格数据地理坐标R,利用geotifwrite写入tif

场景描述&#xff1a; 有时候将nc格式的数据转成tiff&#xff0c;或者是将一个矩阵输出成带有地理坐标信息tiff数据时&#xff0c;常常涉及到空间参考的定义和geotiffwrite()函数。 问题描述&#xff1a; 以全球数据为例&#xff0c;今天发现在matlab中对矩阵进行显示后&…

苹果可能将OpenAI技术集成至iOS/iPadOS 18

&#x1f989; AI新闻 &#x1f680; 苹果可能将OpenAI技术集成至iOS/iPadOS 18 摘要&#xff1a;苹果正在与OpenAI就将GPT技术部署在iOS/iPadOS 18中进行谈判。这项技术被视为可能增强的Siri功能&#xff0c;即“AI聊天机器人”。除Siri外&#xff0c;新技术还可能改善Spotl…