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 自动机能够在 \(\ ...
随机推荐
- virsh常见命令笔记
[基本命令] virsh start 启动 shutdown 关闭 destroy 强制断电 suspend 挂起 resume 恢复 undefine 删除 dominfo 查看配置信息 domif ...
- 【老孟Flutter】为什么 build 方法放在 State 中而不是在 StatefulWidget 中
老孟导读:此篇文章是生命周期相关文章的番外篇,在查看源码的过程中发现了这一有趣的问题,欢迎大家一起探讨. Flutter 中Stateful 组件的生命周期:http://laomengit.com/ ...
- 细说 js 的7种继承方式
在这之前,先搞清楚下面这个问题: function Father(){} Father.prototype.name = 'father'; Father.prototype.children = [ ...
- MySQL45讲笔记-事务隔离级别,为什么你改了数据我看不见
简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败.在MySQL中,事务至此是在引擎层实现的,但并不是所有的MySQL引擎都支持事务,这也是MyISAM被InnoDB取代的原因之一. ...
- 关于使用jq跨域请求的实现
今天算是把js跨域请求搞定了,想实现跨越,首先想到的是JSONP,但是具体去做的时候,发现有很多坑.在本地测试好之后又发现目标网站做了https证书认证,也就是实用的jsonp请求地址必须是https ...
- k8s中教你快速写一条yaml文件
一条yaml中有很多字段,如果去背这些字段,其实也能背过,但是去写一条yaml,也往往浪费很多的时间,也会出错,其实我们可以用一条命令就能快速来写一段自定义的yaml,工作中去修改相应的yaml也得心 ...
- git创建分支并关联远程分支
1.新建本地分支: 如图,再输入你的分支名字,然后选择从哪个远程分支拉代码,如选择master 至此本地分支创建完成. 2.关联远程分支: (1).先输入git branch -vv,看看分支与远程分 ...
- nodejs内网穿透
说明 本地服务注册,基于子域名->端口映射.公网测试请开启二级或三级域名泛解析 无心跳保活.无多线程并发处理 服务器端 请求ID基于全局变量,不支持PM2多进程开服务端.(多开请修改uid函数, ...
- Django orm中related_name/related_query_name区别
related_name/related_query_name区别 class Department(models.Model): title = models.CharField(verbose_n ...
- Redis主从、哨兵模式的搭建
壹.Redis主从分离 准备三个redis配置文件(redis.conf),分别修改为redis6380.conf.redis6381.conf.redis6382.conf 一.配置Master 1 ...