【题目链接】

https://www.luogu.org/problem/P3808

【题意】

给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。

【题解】

不再介绍基础知识了,就是裸的模板题,直接贴上来。

【代码】

 #include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 5e5+;
const int M = 1e6+;
queue < int > Q ;
typedef struct Aho_Corasick_Automaton{
int Son[N][],End[N],Fail[N],idx;
void Init(){
idx = ;
while( !Q.empty() ) Q.pop() ;
memset(Son , , sizeof Son );
memset(End , , sizeof End );
memset(Fail, , sizeof Fail );
}
void Insert(char s[]){
int p = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
if( !Son[p][t] )
Son[p][t] = ++idx;
p = Son[p][t] ;
}
End[p] ++ ;
} void Build(){
for(int i=;i<;i++){
if( Son[][i] )
Fail[Son[][i]] = ,Q.push(Son[][i]);
} while( !Q.empty() ){
int u = Q.front() ;
Q.pop(); for(int i=;i<;i++){
if( Son[u][i] ){
Fail[Son[u][i]] = Son[Fail[u]][i] ;
Q.push( Son[u][i] );
}else{
Son[u][i] = Son[Fail[u]][i];
}
}
}
} void Query(char s[]){
int p = ,res = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
p = Son[p][t] ;
for(int j=p ; j && ~End[j] ; j = Fail[j] ){
res += End[j] ;
End[j] = - ;
}
}
printf("%d\n",res);
}
}AC_Machine ;
AC_Machine AC ;
char s[M];
int n;
int main(){ scanf("%d",&n);
//AC.Init(); for(int i=;i<n;i++){
scanf("%s",s);
AC.Insert(s);
}
AC.Build();
scanf("%s",s);
AC.Query(s);
return ;
}

AC自动机1

【题目链接】

https://www.luogu.org/problem/P3796

【题意】

有N个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串T中出现的次数最多。

和模板题1一样,就是最后结尾的标记加上一些小改动。

【代码】

 // luogu-judger-enable-o2
#pragma GCC optimize(2) #include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5e5+1e4;
const int M = 1e6+;
char Str[][],s[M];
int Cnt[N],vis[N],n;
queue < int > Q ;
typedef struct Aho_Corasick_Automaton{
int Son[N][],End[N],Fail[N],idx;
void Init(){
idx = ;
while( !Q.empty() ) Q.pop() ;
memset(Cnt , , sizeof Cnt );
memset(vis , , sizeof vis );
memset(Son , , sizeof Son );
memset(End , , sizeof End );
memset(Fail, , sizeof Fail );
}
void Insert(char s[],int Id ){
int p = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
if( !Son[p][t] )
Son[p][t] = ++idx;
p = Son[p][t] ;
}
End[p] ++ ;
vis[p] = Id;
} void Build(){
for(int i=;i<;i++){
if( Son[][i] )
Fail[Son[][i]] = ,Q.push(Son[][i]);
} while( !Q.empty() ){
int u = Q.front() ;
Q.pop(); for(int i=;i<;i++){
if( Son[u][i] ){
Fail[Son[u][i]] = Son[Fail[u]][i] ;
Q.push( Son[u][i] );
}else{
Son[u][i] = Son[Fail[u]][i];
}
}
}
} void Query(char s[]){
int p = ,res = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
p = Son[p][t] ;
for(int j=p ; j ; j = Fail[j] ){
if( End[j] ){
Cnt[vis[j]] ++ ;
}
}
}
for(int i=;i<n;i++){
res = max( Cnt[i] , res ) ;
}
cout << res << endl;
//printf("%d\n",res);
for(int i=;i<n;i++){
if( res == Cnt[i] ){
cout << Str[i] << endl;
}
}
}
}AC_Machine ;
AC_Machine AC ; int main(){ ios_base :: sync_with_stdio(false);
cin.tie(NULL) , cout.tie(NULL); while ( cin >> n ,n ){
AC.Init();
for(int i=;i<n;i++){
//scanf("%s",s);
cin >> Str[i];
AC.Insert(Str[i],i);
}
AC.Build();
//scanf("%s",s);
cin >> s ;
AC.Query(s);
}
return ;
}

AC自动机2

【题目链接】

https://www.luogu.org/problem/P5357

【题意】

给你一个文本串 SS 和 nn 个模式串 T_{1..n},请你分别求出每个模式串 Ti​ 在 S 中出现的次数。

【题解】

这个题目就非常有意思了,

其实我还没怎么想就去洛谷看题解了,

发现是还是各显神通呀。大家都很厉害,我这里提供三个人的做法。

其实道理都是一样的。

一种是:进行类似于差分累加的过程。

 #include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5+;
const int M = 2e6+;
typedef struct node{
int son[],fail;
int& operator [] (int x) {return son[x];}
}Node; Node Trie[N]; int Q[N],head[N],Next[N],Ans[N],d[N];
int Head,Tail,n,idx;
char s[M]; void Insert(char s[],int Id ){
int p = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
if( !Trie[p][t] )
Trie[p][t] = ++idx;
p = Trie[p][t] ;
}
Next[Id] = head[p] ;
head[p] = Id;
} void Build(){
Head = , Tail = ;
for(int i=;i<;i++) if( Trie[][i] ) Q[++Tail] = Trie[][i];
while( Head <= Tail ){
int u = Q[Head++] ;
for(int i=;i<;i++){
if( Trie[u][i] ){
Trie[Trie[u][i]].fail = Trie[Trie[u].fail][i] ;
Q[++Tail] = Trie[u][i] ;
}else{
Trie[u][i] = Trie[Trie[u].fail][i];
}
}
}
} void Query(char s[]){
int p = ,res = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
p = Trie[p][t] ;
d[p] ++ ;
}
for(int i=idx;i;i--){
for(int j=head[Q[i]] ; j ; j = Next[j] ) Ans[j] = d[Q[i]];
d[ Trie[Q[i]].fail ] += d[ Q[i] ];
}
for(int i=;i<=n;i++){
cout << Ans[i] << endl ;
}
} int main(){ ios_base :: sync_with_stdio(false);
cin.tie(NULL) , cout.tie(NULL); cin >> n;
for(int i=;i<=n;i++){
cin >> s;
Insert(s,i);
}
Build();
cin >> s ;
Query(s);
return ;
}

AC自动机3—差分做法

一种是拓扑排序的做法

 #include<bits/stdc++.h>
using namespace std;
const int N = 2e6+;
typedef struct Node {
int son[],Fail,End,Cnt;
void Clear(){
memset(son,,sizeof son );
Fail = End = Cnt = ;
}
int & operator [] (int x){ return son[x];}
}Node ;
Node Trie[N];
char s[N];
int Ans,idx=,n,Head,Tail;
int Mp[N],vis[N],Ind[N],Q[N]; void Insert( char s[] , int Id ){
int p = ;
for(int i=;s[i];i++){
int t = s[i] - 'a';
if( !Trie[p][t] )
Trie[p][t] = ++idx ;
p = Trie[p][t] ;
}
if( !Trie[p].End ) Trie[p].End = Id;
Mp[Id] = Trie[p].End
;
} void Build(){
Head = , Tail = ;
for(int i=;i<;i++){ Trie[][i] = ; }
Q[++Tail] = ;
while( Head <= Tail ){
int u = Q[Head++] ;
for(int i=;i<;i++){
int To = Trie[u][i] ;
if( To ){
Trie[To].Fail = Trie[Trie[u].Fail][i];
Ind[ Trie[To].Fail ] ++ ;
Q[++Tail] = Trie[u][i];
}else{
Trie[u][i] = Trie[Trie[u].Fail][i];
}
}
}
}
void Topu( ){
Head = , Tail = ;
for(int i=;i<=idx;i++) if( !Ind[i] ) Q[++Tail] = i; while( Head <= Tail ){
int u = Q[Head++];
vis[ Trie[u].End ] = Trie[u].Cnt ;
int To = Trie[u].Fail ;
Ind[To] -- ;
Trie[To].Cnt += Trie[u].Cnt;
if( Ind[To] == )
Q[++Tail] = To ;
}
}
void Query(char s[]){
int p = ;
for(int i=;s[i];i++){
p = Trie[p][s[i]-'a'];
Trie[p].Cnt ++ ;
}
} int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",s);
Insert( s, i );
}
Build() ;
scanf("%s",s);
Query(s);
Topu();
for(int i=;i<=n;i++){
printf("%d\n",vis[Mp[i]]);
}
} /* 5
a
bb
aa
abaa
abaaa
abaaabaa 6
0
3
2
1 */

AC自动机3—拓扑排序

最后一种是最正宗的做法了,建fail树。

 #include<bits/stdc++.h>
using namespace std; const int N = 2e6+;
void dfs(int u);
void Add_edge(int u,int v); char s[N];
int head[N],nxt[N],to[N], cnt ; int n,idx=;
int Trie[N][],fail[N],Match[N],Sz[N]; int Q[N],Head,Tail; void Insert(char s[] ,int Id){
int p = ;
for(int i=;s[i];i++){
int t = s[i]-'a';
if( !Trie[p][t] ){
Trie[p][t] = ++idx;
}
p = Trie[p][t] ;
}
Match[Id] = p ;
}
void Build(){
Head = , Tail = ;
for(int i=;i<;i++)
Trie[][i] = ;
Q[++Tail] = ;
while( Head <= Tail ){
int u = Q[Head++] ;
for(int i=;i<;i++){
if( Trie[u][i] ){
fail[Trie[u][i]] = Trie[fail[u]][i];
Q[++Tail] = Trie[u][i];
}else{
Trie[u][i] = Trie[fail[u]][i];
}
}
}
}
void Query(char s[]){
for(int p=,i=;s[i];i++){
p = Trie[p][s[i]-'a'];
Sz[p] ++ ;
}
for(int i=;i<=idx;i++){
Add_edge( fail[i] , i );
}
dfs( );
for(int i=;i<=n;i++){
printf("%d\n",Sz[Match[i]]);
}
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",s);
Insert(s,i);
}
Build();
scanf("%s",s);
Query(s);
return ;
}
void dfs(int u){
for(int i=head[u];i;i=nxt[i]){
int v = to[i] ;
dfs(v);
Sz[u] += Sz[v];
}
}
void Add_edge(int u,int v){
nxt[++cnt]= head[u];
head[u] = cnt ;
to[cnt] = v ;
} /* 5
a
bb
aa
abaa
abaaa
abaaabaa 6
0
3
2
1 */

AC自动机3—fail树

【AC自动机】洛谷三道模板题的更多相关文章

  1. 【最大流ISAP】洛谷P3376模板题

    题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点序号.汇点序号. 接下来M行每行 ...

  2. 【后缀数组】洛谷P3809模板题

    题目背景 这是一道模板题. 题目描述 读入一个长度为 n n n 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置. ...

  3. cjoj P1435 - 【模板题 USACO】AC自动机 && 洛谷 P3796 【模板】AC自动机(加强版)

    又打了一遍AC自动稽. 海星. 好像是第一次打trie图,很久以前就听闻这个思想了.OrzYYB~ // It is made by XZZ #include<cstdio> #inclu ...

  4. 【后缀自动机】洛谷P3804模板题

    题目描述 给定一个只包含小写字母的字符串SSS, 请你求出 SSS 的所有出现次数不为 111 的子串的出现次数乘上该子串长度的最大值. 输入输出格式 输入格式: 一行一个仅包含小写字母的字符串SSS ...

  5. 【AC自动机】HDU中模板题

    [HDU2222] 最纯粹的裸题,错误点详见注释. #include<iostream> #include<cstdio> #include<cstring> #i ...

  6. LCA算法倍增算法(洛谷3379模板题)

    倍增(爬树)算法,刚刚学习的算法.对每一个点的父节点,就记录他的2k的父亲. 题目为http://www.luogu.org/problem/show?pid=3379 第一步先记录每一个节点的深度用 ...

  7. 洛谷 P4148 简单题 KD-Tree 模板题

    Code: //洛谷 P4148 简单题 KD-Tree 模板题 #include <cstdio> #include <algorithm> #include <cst ...

  8. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  9. 【noip】跟着洛谷刷noip题2

    noip好难呀. 上一个感觉有点长了,重开一个. 36.Vigenère 密码 粘个Openjudge上的代码 #include<cstdio> #include<iostream& ...

随机推荐

  1. Redis使用Docker镜像安装

    详细见本人以下文档: https://www.cnblogs.com/zyc-blogs/p/9621727.html

  2. Hadoop环境搭建|第三篇:spark环境搭建

    一.环境搭建 1.1.上传spark安装包 创建文件夹用于存放spark安装文件命令:mkdir spark 1.2.解压spark安装包 命令:tar -zxvf spark-2.1.0-bin-h ...

  3. JS -- Unexpected trailing comma

    Unexpected trailing comma 后面多了一个逗号

  4. java获取远程图片分辨率

    package com.haiyisoft.hyoaPc; import java.awt.image.BufferedImage;import java.io.IOException;import ...

  5. 安装RabbitMQ管理插件失败

    运行 rabbitmq-plugins.bat enable rabbitmq_management后提示失败信息  是因为erlang和RabbitMQ版本冲突导致

  6. onNewIntent

    当Activity不是Standard模式,并且被复用的时候,会触发onNewIntent(Intent intent) 这个方法,一般用来获取新的Intent传递的数据 我们一般会把MainAcit ...

  7. js 删除 按钮所在的行

    <body> <table id="delte"> <caption>简易购物车</caption> <tr> < ...

  8. 一个数独引发的惨案:零知识证明(Zero-Knowledge Proof)

    导言:原文的作者是著名的Ghost和Spectre 这两个协议的创始团队的领队Aviv Zohar.原文作者说他的这篇原文又是引用了以下这两篇学术论文: How to Explain Zero Kno ...

  9. [System.Serializable],

    [System.Serializable]添加在类,枚举,结构前面,可以让该这些对象在inspector中显示 [SerializeField]是设置非public 成员对象在inspector中显示

  10. 安卓运行linux应用程序

    安卓是可以运行linux应用程序的,安卓系统原来就基于Linux.但是安卓已经把linux改头换面了.具体方法是安装Termux软件,然后就可以运行pkg命令安装软件包了,希望可以帮助到大家.