【题目链接】

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. zabbix监控项整理Items-key

    agent.hostname:hostname,字符串 agent.ping:可用性检查,可用返回1:不可用返回空 agent.version:agent程序的版本,返回字符串 kernel.maxf ...

  2. React的Virtual DOM厉害了

    React 的伟大之处就在于,提出了Virtual DOM这种新颖的思路,并且这种思路衍生出了React Native,有可能会统一Web/Native开发. 在性能方面,由于用到了Virtual D ...

  3. Flutter移动电商实战 --(30)列表页_商品列表UI界面布局

    小程序里面的布局方式 小程序的图片上这里使用的是warp布局,因为首页里面火爆专区,已经用过了warp来布局了. 所以这里我们没有必要再讲一遍,这里我们使用ListView,我们把它布局成下图这种形式 ...

  4. APP_DEBUG改成false上线之后发现:“页面错误!请稍后再试~

      TP框架 页面错误!请稍后再试 把APP_DEBUG改成false上线之后发现:"页面错误!请稍后再试-". 问题一般是出在,display() 指定某个具体的模板文件后win ...

  5. pre-fork 分叉 软分叉 硬分叉 前叉实现 pre-fork implementation

    https://mp.weixin.qq.com/s/wIDTs2J1ZkLkAEHqQnkYnw 什么是分叉?为何对区块链发展至关重要? Uselink公有链 Uselink公有链 2018-12- ...

  6. 【转】netty4.1.32 pipeline的添加顺序和执行顺序

    原文:https://www.cnblogs.com/ruber/p/10186571.html 本文只想讨论一下pipeline的执行顺序问题,因为这个搞不明白就不知道先添加编码还是解码,是不是可以 ...

  7. CentOS源码安装 Tomcat/8.0.24

    依个人的习惯,喜欢将源码安装在/usr/local这个目录下面: 第一步:下载源码 wget http://archive.apache.org/dist/tomcat/tomcat-8/v8.0.2 ...

  8. PS改变图片像素大小(一寸照片变二寸)

    1.打开Photoshop,点击“文件”菜单,选择“打开”,将要处理的图片加载进来 2.找到“图像”菜单,选择“图像大小”,我们在这里对图片进行处理. 3.根据需要对其分辨率或者尺寸进行设置,设置好后 ...

  9. microsoft 官方学习资源

    https://devblogs.microsoft.com/dotnet/  :_NET Blog https://docs.microsoft.com/zh-cn/learn/ :Microsof ...

  10. Qt编写自定义控件25-自定义QCustomPlot

    一.前言 上次在写大屏数据可视化电子看板系统时候,提到过改造QCustomPlot来实现柱状分组图.横向柱状图.横向分组图.鼠标悬停提示等.这次单独列出来描述,有很多人疑问为啥不用QChart,或者e ...