AC自动机——看似KMP在跑,其实fail在跳
先存代码
AC自动机(简单版)
#include<bits/stdc++.h>
#define maxn 1000007
using namespace std;
int n,ans;
int tr[maxn][28],val[maxn],cnt,fail[maxn];
char mod[maxn],tx[maxn];
queue<int >q; void build(char *a){
int now=0;
for(int i=0;a[i];i++){
if(!tr[now][a[i]-'a']) tr[now][a[i]-'a']=++cnt;
now=tr[now][a[i]-'a'];
}
val[now]++;
}//建树 void AC(){
for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);//26个字母跑
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++){
if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);//调取指针
else tr[u][i]=tr[fail[u]][i];//建立“虚边”——指向失配指针的i边
//这里已经改变了trie图
}
}
} int query(char *t){
int ol=0,u=0;
for(int i=0;t[i];i++){
u=tr[u][t[i]-'a'];
for(int j=u;j&&val[j]!=-1;j=fail[j])
ol+=val[j],val[j]=-1;//fail跳(这样其实很慢)
}
return ol;
} int main(){
// freopen("cin.in","r",stdin);
// freopen("co.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%s",mod),build(mod);
AC();
scanf("%s",tx);ans=query(tx);
printf("%d\n",ans);
}
AC自动机(加强版)
#include<bits/stdc++.h>
#define maxn 1000007
using namespace std;
int T,n,ans;
char mod[maxn][100],tx[maxn]; namespace AC{
int tr[maxn][27],fail[maxn],tot;
int cnt,val[maxn],num[maxn];
void Init(){
memset(tr,0,sizeof(tr));
memset(num,0,sizeof(num));
memset(fail,0,sizeof fail);
memset(val,0,sizeof val);
cnt=ans=0;
}
void insert(char *s,int id){
int now=0;
for(int i=0;s[i];i++){
if(!tr[now][s[i]-'a']) tr[now][s[i]-'a']=++cnt;
now=tr[now][s[i]-'a'];
}
val[now]=id;//记录id,这个不怕覆盖
}
queue<int >q;
void build(){
for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]),cerr<<fail[tr[u][i]]<<endl;
else tr[u][i]=tr[fail[u]][i];
}
}
void query(char *t){
int u=0;
for(int i=0;t[i];i++){
u=tr[u][t[i]-'a'];
for(int j=u;j;j=fail[j])
if(val[j]) num[val[j]]++,ans=max(ans,num[val[j]]);
}//还是跳,不过记录的不一样而已
printf("%d\n",ans);
for(int i=1;i<=n;i++) if(ans==num[i]) printf("%s\n",mod[i]);
}
} int main(){
scanf("%d",&n);
while(n){
AC::Init();
for(int i=1;i<=n;i++) scanf("%s",mod[i]),AC::insert(mod[i],i);
AC::build();
scanf("%s",tx);
AC::query(tx);
scanf("%d",&n);
}
}
AC自动机(二次加强版)
#include<bits/stdc++.h>
#define maxn 2000007
using namespace std;
int n;
char mod[maxn],tx[maxn];
int fail[maxn],tr[maxn][27],val[maxn],num[maxn];
int id[maxn],cnt,in[maxn],to[maxn]; void insert(char *a,int idx){
int now=0;
for(int i=0;a[i];i++){
if(!tr[now][a[i]-'a']) tr[now][a[i]-'a']=++cnt;
now=tr[now][a[i]-'a'];
}
val[now]++;id[idx]=now;//记录
} queue<int >q; void build(){
for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++){
if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]),in[fail[tr[u][i]]]++;
else tr[u][i]=tr[fail[u]][i];
}
}
} void query(char *t){
int u=0;
for(int i=0;t[i];i++)
u=tr[u][t[i]-'a'],num[u]++;
} void topu(){
for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i);
while(!q.empty()){
int u=q.front(),v=fail[u];q.pop();
num[v]+=num[u];--in[v];
if(!(in[v])) q.push(v);
}
}//这里是跟题解学的topu,效率也挺高 int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",mod),insert(mod,i);
build();
scanf("%s",tx);query(tx);
topu();
for(int i=1;i<=n;i++) printf("%d\n",num[id[i]]);
}
单词
#include<bits/stdc++.h>
#define maxn 2000007
using namespace std;
int n;
char mod[maxn],tx[maxn],c[maxn];
int fail[maxn],tr[maxn][28],val[maxn],num[maxn];
int id[maxn],cnt,in[maxn],tot; void insert(char *a,int idx){
int now=0;
for(int i=0;a[i];i++){
if(!tr[now][a[i]-'a']) tr[now][a[i]-'a']=++cnt;
now=tr[now][a[i]-'a'];
}
val[now]++;id[idx]=now;
} queue<int >q; void build(){
for(int i=0;i<27;i++) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<27;i++){
if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]),in[fail[tr[u][i]]]++;
else tr[u][i]=tr[fail[u]][i];
}
}
} void query(char *t){
int u=0;
for(int i=0;t[i];i++)
u=tr[u][t[i]-'a'],num[u]++;
} void topu(){
for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i);
while(!q.empty()){
int u=q.front(),v=fail[u];q.pop();
num[v]+=num[u];--in[v];
if(!(in[v])) q.push(v);
}
} void work(char *a,char *b){
int len1=strlen(a),len2=strlen(b);
for(int i=len1;i<len1+len2;i++)
a[i]=b[i-len1];
} int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",c),work(mod,c),
tot=strlen(mod),insert(c,i),mod[tot++]='{';
build();
query(mod);topu();
for(int i=1;i<=n;i++) printf("%d\n",num[id[i]]);
}
AC自动机——看似KMP在跑,其实fail在跳的更多相关文章
- AC自动机及KMP练习
好久都没敲过KMP和AC自动机了.以前只会敲个kuangbin牌板子套题.现在重新写了自己的板子加深了印象.并且刷了一些题来增加自己的理解. KMP网上教程很多,但我的建议还是先看AC自动机(Trie ...
- 2017多校第8场 HDU 6138 Fleet of the Eternal Throne AC自动机或者KMP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6138 题意:给n个串,每次询问x号串和y号串的最长公共子串的长度,这个子串必须是n个串中某个串的前缀 ...
- AC自动机讲解
今天花了半天肝下AC自动机,总算啃下一块硬骨头,熬夜把博客赶出来.. 正如许多博客所说,AC自动机看似很难很妙,而事实上不难,但的确很妙.笼统地说,AC自动机=Trie+KMP,但是仅仅知道这个并没有 ...
- 初学AC自动机
前言 一直听说\(AC\)自动机是一个很难很难的算法,而且它不在\(NOIP\)提高组范围内(这才是关键),所以我一直没去学. 最近被一些字符串题坑得太惨,于是下定决心去学\(AC\)自动机. 简介 ...
- AC自动机简明教程
不会kmp和Trie树的请点击右上角X. AC自动机与kmp的唯一区别便是从单模式串变成了多模式串. 那么与kmp相同,AC自动机中的fail指针是指向当前状态的最长后缀. 当然这个后缀要在Trie树 ...
- AC自动机讲解超详细
begin:2019/5/2 感谢大家支持! AC自动机详细讲解 AC自动机真是个好东西!之前学KMP被Next指针搞晕了,所以咕了许久都不敢开AC自动机,近期学完之后,发现AC自动机并不是很难,特别 ...
- 【模版 Luogu P3808/P3796/P5357】AC自动机(简论)
浙江集训Day9,没有出任何实质性成果,只好把昨天打完的板子记一下. 该博客基于luogu的三道模版题.只有一个大致的讲解,主要提供代码给自己参考. ------------------------- ...
- 「笔记」AC 自动机
目录 写在前面 定义 引入 构造 暴力 字典图优化 匹配 在线 离线 复杂度 完整代码 例题 P3796 [模板]AC 自动机(加强版) P3808 [模板]AC 自动机(简单版) 「JSOI2007 ...
- AC 自动机学习笔记
虽然 NOIp 原地爆炸了,目前进入 AFO 状态,但感觉省选还是要冲一把,所以现在又来开始颓字符串辣 首先先复习一个很早很早就学过但忘记的算法--自动 AC AC自动机. AC 自动机能够在 \(\ ...
随机推荐
- Spring Boot 2.0 的配置绑定类Bindable居然如此强大
1. 前言 在开发Spring Boot应用时会用到根据条件来向Spring IoC容器注入Bean.比如配置文件存在了某个配置属性才注入Bean : 图中红色的部分是说,只有ali.pay.v1.a ...
- 记一次Goroutine与wg导致的问题
前言 今天发现了一个问题是之前一直没有注意到的,这里记一下 正文 Send Closed Chan 问题概述 代码逻辑是启动时启动多个 channel, channel1 获取数据监听数据处理后发送给 ...
- 【Flutter】功能型组件之异步UI更新
前言 很多时候会依赖一些异步数据来动态更新UI,比如在打开一个页面时我们需要先从互联网上获取数据,在获取数据的过程中我们显示一个加载框,等获取到数据时我们再渲染页面:又比如想展示Stream(比如文件 ...
- 1018 Public Bike Management (30分) PAT甲级真题 dijkstra + dfs
前言: 本题是我在浏览了柳神的代码后,记下的一次半转载式笔记,不经感叹柳神的强大orz,这里给出柳神的题解地址:https://blog.csdn.net/liuchuo/article/detail ...
- docker 删除和拉取镜像
删除镜像 # docker rmi -f 镜像id # 删除指定镜像 docker rmi -f 25d5f6s564 # docker rmi -f 镜像id 镜像id # 删除多个镜像 docke ...
- (十八)configparser模块
configparser模块一般是用来处理配置文件的,如: [DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel ...
- LeetCode965. 单值二叉树
题目 1 class Solution { 2 public: 3 int flag = 0; 4 bool isUnivalTree(TreeNode* root){ 5 isUnivalTree1 ...
- cisco交换机路由器静态路由配置
一.切换模式 router>en //用户模式enable router#conf t //特权模式 ...
- 为了更好的多线程性能,在对象创建或者更新时,若数据大于2047字节则 Python 的 GIL 会被释放。 执行计算密集型任务如压缩或哈希时释放 GIL
hashlib - Secure hashes and message digests - Python 3.8.3 documentation https://docs.python.org/3.8 ...
- Python学习【第6篇】:集合的定义和基本方法
1.概念 (1)不同元素组成 例: s = {1,2,3,4,4,4,4,4,4}print(s)运行结果:{1, 2, 3, 4}因为是不同元素组成,因此去重了 (2)无序 例: s = {&quo ...