模板1. 给出模式串和文本串,文本串长度小于1e6,模式串长度之和小于1e6,求文本串中有多少模式串出现。

题目链接:https://www.luogu.org/problem/P3808

AC code:

/*
luoguP3808 (AC自动机模板题)
求文本串中有多少模式串出现
*/
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std; const int maxn=1e6+;
//key表示有多少个单词以该结点结尾
int fail[maxn],trie[maxn][],key[maxn];
int n,cnt;
char s[maxn];
//在Trie树中插入结点
void build(char *s){
int len=strlen(s),u=;
for(int i=;i<len;++i){
int t=s[i]-'a';
if(!trie[u][t]){
++cnt;
memset(trie[cnt],,sizeof(trie[cnt]));
fail[cnt]=,key[cnt]=;
trie[u][t]=cnt;
}
u=trie[u][t];
}
key[u]+=;
}
//构建fail指针
void get_fail(){
queue<int> que;
for(int i=;i<;++i){ //提前处理第二层
if(trie[][i]){
fail[trie[][i]]=; //指向根节点
que.push(trie[][i]);
}
}
while(!que.empty()){ //bfs求所有子结点
int u=que.front();
que.pop();
for(int i=;i<;++i){
if(trie[u][i]){
fail[trie[u][i]]=trie[fail[u]][i];
que.push(trie[u][i]);
}
else{
trie[u][i]=trie[fail[u]][i];
}
}
}
}
//AC自动机匹配
int query(char *s){
int len=strlen(s),u=,ans=;
for(int i=;i<len;++i){
int t=s[i]-'a';
u=trie[u][t];
for(int j=u;j&&key[j]!=-;j=fail[j]){ //循环求解
ans+=key[j];
key[j]=-;
}
}
return ans;
} int main(){
memset(trie[],,sizeof(trie[]));
key[]=;
cnt=;
scanf("%d",&n);
for(int i=;i<=n;++i){
scanf("%s",s);
build(s);
}
fail[]=; //结束标志
get_fail(); //求失配指针
scanf("%s",s);
printf("%d\n",query(s));
return ;
}

模板2.  luoguP5357(二次增强),输出模式串在文本串中出现的次数(有相同的模式串)(拓扑排序优化AC自动机)

  与增强版有两个改进地方。

  第一:有相同的模式串,所以每个结点不能直接用key存储以该结点结束的模式串下标。这里给结点一个标记key[u],这个标记是以该结点为结束的模式串的最小下标key[u]=k,之后以该结点结束的其它字符串都记录此标记ind[k]=key[u],输出时按标记输出即可ans[ind[i]]。

  第二:如果数据毒瘤,比如aaaaa...aaa,那么这样暴力去跳fail的最坏时间复杂度是O(模式串长度 · 文本串长度),因为对于每一次跳fail我们都只使深度减1,那样深度(深度最深是模式串长度)是多少,每一次跳的时间复杂度就是多少。那么还要乘上文本串长度,就几乎是 O(模式串长度 · 文本串长度)的了。

  但是模板为什么复杂度是O(模式串总长)呢?因为每一个Trie上的点都只会经过一次(打了标记fail=-1),但这里每一个点就不止经过一次了,所以时间复杂度就爆炸了。

  解决办法是应用拓扑排序,也就是对结点计数时先只是添加计数值res,并不暴力去跳fail,等query结束之后,利用拓扑排序去将计数值上传。详见代码(有注释)

AC code:

/*
luoguP5357(二次增强)
输出模式串在文本串中出现的次数
拓扑排序优化AC自动机
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cstdlib>
#include<string>
using namespace std; const int maxn=2e6+;
char s[maxn];
//ind[i]记录模式串i所在结点的标记
int n,ans[],ind[],cnt=;
//key[u]记录结点u的标记,该标记可能对应多个模式串(可能有相同的模式串)
//in[u]记录结点u的入度,res[u]记录结点u被加的次数
int fail[maxn],trie[maxn][],key[maxn],in[maxn],res[maxn]; void build(char *s,int k){
int len=strlen(s),u=;
for(int i=;i<len;++i){
int t=s[i]-'a';
if(!trie[u][t]){
++cnt;
memset(trie[cnt],,sizeof(trie[cnt]));
fail[cnt]=,key[cnt]=,in[cnt]=,res[cnt]=;
trie[u][t]=cnt;
}
u=trie[u][t];
}
//每个结点最多被标记一次
if(!key[u]) key[u]=k;
ind[k]=key[u]; //模式串k所在结点的标记为key[u]
} void get_fail(){
queue<int> que;
for(int i=;i<;++i){
if(trie[][i]){
fail[trie[][i]]=;
que.push(trie[][i]);
}
}
while(!que.empty()){
int u=que.front();que.pop();
for(int i=;i<;++i){
if(trie[u][i]){
fail[trie[u][i]]=trie[fail[u]][i];
++in[fail[trie[u][i]]]; //入度加一
que.push(trie[u][i]);
}
else{
trie[u][i]=trie[fail[u]][i];
}
}
}
} void query(char *s){
int len=strlen(s),u=;
for(int i=;i<len;++i){
int t=s[i]-'a';
u=trie[u][t];
++res[u]; //不用循环查询,直接计数加的次数
}
} void topu(){ //拓扑排序
queue<int> que;
for(int i=;i<=cnt;++i)
if(!in[i])
que.push(i); //入队
while(!que.empty()){
int u=que.front();que.pop();
ans[key[u]]=res[u]; //将结点u的计数值加到ans数组上
int v=fail[u];--in[v]; //减入度
res[v]+=res[u]; //将计数值传递
if(!in[v]) que.push(v);
}
} int main(){
scanf("%d",&n);
memset(trie[],,sizeof(trie[]));
cnt=,key[]=,in[]=,res[]=;
for(int i=;i<=n;++i){
scanf("%s",s);
build(s,i);
}
fail[]=; // 结束标志
get_fail(); //得到fail数组
scanf("%s",s);
query(s);
topu();
for(int i=;i<=n;++i) //输出
printf("%d\n",ans[ind[i]]);
return ;
}

(模板)AC自动机模板的更多相关文章

  1. HDU 2222 AC自动机模板题

    题目: http://acm.hdu.edu.cn/showproblem.php?pid=2222 AC自动机模板题 我现在对AC自动机的理解还一般,就贴一下我参考学习的两篇博客的链接: http: ...

  2. Match:Keywords Search(AC自动机模板)(HDU 2222)

    多模匹配 题目大意:给定很多个字串A,B,C,D,E....,然后再给你目标串str字串,看目标串中出现多少个给定的字串. 经典AC自动机模板题,不多说. #include <iostream& ...

  3. HDU 3065 (AC自动机模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3065 题目大意:多个模式串,范围是大写字母.匹配串的字符范围是(0~127).问匹配串中含有哪几种模 ...

  4. HDU 2896 (AC自动机模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2896 题目大意:多个模式串.多个匹配串.其中串的字符范围是(0~127).问匹配串中含有哪几个模式串 ...

  5. HDU 2222(AC自动机模板题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意:多个模式串.问匹配串中含有多少个模式串.注意模式串有重复,所以要累计重复结果. 解题 ...

  6. HDU 2222 (AC自动机模板题)

    题意: 给一个文本串和多个模式串,求文本串中一共出现多少次模式串 分析: ac自动机模板,关键是失配函数 #include <map> #include <set> #incl ...

  7. hdu 2222 Keywords Search ac自动机模板

    题目链接 先整理一发ac自动机模板.. #include <iostream> #include <vector> #include <cstdio> #inclu ...

  8. KMP与AC自动机模板

    HDU 1711 Number Sequence(KMP模板题) http://acm.hdu.edu.cn/showproblem.php?pid=1711 #include<bits/std ...

  9. HDU3695(AC自动机模板题)

    题意:给你n个字符串,再给你一个大的字符串A,问你着n个字符串在正的A和反的A里出现多少个? 其实就是AC自动机模板题啊( ╯□╰ ) 正着query一次再反着query一次就好了 /* gyt Li ...

  10. POJ2222 Keywords Search AC自动机模板

    http://acm.hdu.edu.cn/showproblem.php?pid=2222 题意:给出一些单词,求多少个单词在字符串中出现过(单词表单词可能有相同的,这些相同的单词视为不同的分别计数 ...

随机推荐

  1. git 导出远程特定分之

    很多时候,git clone 只是 clone 下来了 master 分支,如果想 clone 特定分支.有的时候不知如何是好. 找到了如下的命令,记录一下.以便有需要的同学可以使用. git co ...

  2. C#题(子文章)(持续更新)

    -----> 总文章 入口 文章目录 [-----> 总文章 入口](https://blog.csdn.net/qq_37214567/article/details/90174445) ...

  3. Linux下的crontab定时执行任务命令

    0x00 简介 在LINUX中,周期执行的任务一般由cron这个守护进程来处理[ps -ef|grep cron].cron读取一个或多个配置文件,这些配置文件中包含了命令行及其调用时间. cron的 ...

  4. 5、vueJs基础知识05

    vue2.0相比于1.0的变化 1.在每个组件模板中,不再支持片段代码,需要一个根元素包裹 组件中模板: 之前: <template> <h3>我是组件</h3>& ...

  5. html5中hgroup和address标签使用总结

    html5中hgroup和address标签使用总结 一.总结 一句话总结: hgroup元素(不推荐使用):用来给标题分组,通常放在header中: address元素:斜体显示:用来说明作者的联系 ...

  6. python 输出‘\xe8\xb4\x9d\xe8\xb4\x9d’, ‘\xe6\x99\xb6\xe6\x99\xb6’, ‘\xe6\xac\xa2\xe6\xac\xa2’]

    如上代码块,结果输出为: [‘\xe8\xb4\x9d\xe8\xb4\x9d’, ‘\xe6\x99\xb6\xe6\x99\xb6’, ‘\xe6\xac\xa2\xe6\xac\xa2’] 北京 ...

  7. Unity编辑器环境在Inspector面板中显示变量

    Serialize功能Unity3D 中提供了非常方便的功能可以帮助用户将 成员变量 在Inspector中显示,并且定义Serialize关系. 简单的说,在没有自定义Inspector的情况下所有 ...

  8. MySQL参数: innodb_flush_log_at_trx_commit和sync_binlog

    innodb_flush_log_at_trx_commit 当innodb_flush_log_at_trx_commit=0时, log buffer将每秒一次地写入log file, 并且log ...

  9. Hystrix原理与实战(转)

    背景 分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务.如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服 ...

  10. 【转载】 深度学习之卷积神经网络(CNN)详解与代码实现(一)

    原文地址: https://www.cnblogs.com/further-further-further/p/10430073.html ------------------------------ ...