P3796 【模板】AC自动机(加强版)

题目描述

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

输入输出格式

输入格式:

输入含多组数据。

每组数据的第一行为一个正整数,表示共有个模式串,

接下去行,每行一个长度小于等于的模式串。下一行是一个长度小于等于的文本串

输入结束标志为

输出格式:

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

输入输出样例

输入样例#1:

2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0

输出样例#1:

4
aba
2
alpha
haha

就是个纯模板,没啥好说的,。

不过AC自动机真的是,,恶心到爆,,,

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<queue>
  6 #include<algorithm>
  7 #include<map>
  8 using namespace std;
  9 const int MAXN=100001;
 10 void read(int &n)
 11 {
 12     char c=‘+‘;int x=0;bool flag=0;
 13     while(c<‘0‘||c>‘9‘){c=getchar();if(c==‘-‘)flag=1;}
 14     while(c>=‘0‘&&c<=‘9‘){x=x*10+c-48;c=getchar();}
 15     flag==1?n=-x:n=x;
 16 }
 17 char s[MAXN][101];
 18 char text[1000001];
 19 struct AC_Automata
 20 {
 21     int sz;
 22     int ch[MAXN][28];//trie树
 23     int cnt[MAXN];// 每个单词出现的次数
 24     int val[MAXN];// 记录是否有单词
 25     int last[MAXN];
 26     int f[MAXN];
 27     int sigma_sz;
 28     map<string,int>mp;//出现的位置
 29     void Init()
 30     {
 31         memset(ch[0],0,sizeof(ch[0]));
 32         memset(cnt,0,sizeof(cnt));
 33         mp.clear();
 34         sz=1;
 35         sigma_sz=26;
 36     }
 37     int idx(char c)
 38     {
 39         return c-‘a‘;
 40     }
 41     void Insert(char *s,int pos)
 42     {
 43         int l=strlen(s); int now=0;
 44         for(int i=0;i<l;i++)
 45         {
 46             int c=idx(s[i]);
 47             if(!ch[now][c])
 48             {
 49                 memset(ch[sz],0,sizeof(ch[sz]));
 50                 val[sz]=0;
 51                 ch[now][c]=sz++;
 52             }
 53             now=ch[now][c];
 54         }
 55         val[now]=pos;//一个单词的结束
 56         mp[string(s)]=pos; // 记录这个字符串出现的位置,按顺序输出
 57     }
 58     void getfail()
 59     {
 60         f[0]=0;
 61         queue<int>q;
 62         for(int i=0;i<sigma_sz;i++)
 63         {
 64             int u=ch[0][i];
 65             if(u)// 此处有单词出现
 66             {
 67                 f[u]=0;// 连向根节点
 68                 q.push(u);
 69                 last[u]=0;// 上一个出现的单词一定是根节点
 70                 // 上面两条语句没什么卵用,只是为了帮助理解AC自动机的含义
 71             }
 72         }
 73         while(!q.empty())
 74         {
 75             int p=q.front();q.pop();
 76             for(int i=0;i<sigma_sz;i++)
 77             {
 78                 int u=ch[p][i];
 79                 if(!u)//没有孩子
 80                 {
 81                     ch[p][i]=ch[f[p]][i];// 补上一条不存在的边,减少查找次数
 82                     continue;
 83                 }
 84                 q.push(u);
 85                 int v=f[p];// 找到它的失陪指针
 86                 f[u]=ch[v][i];//因为我们在上面补上了一条不存在边,所以他一定存在孩子
 87                 last[u]=val[f[u]] ? f[u] : last[f[u]];
 88                 //判断下是不是单词结尾  是, 不是
 89             }
 90         }
 91     }
 92     int ok(int pos)
 93     {
 94         if(pos)
 95         {
 96             cnt[val[pos]]++;
 97             ok(last[pos]);
 98         }
 99     }
100     void find(char *s)// 查找模式串
101     {
102         int l=strlen(s);
103         int j=0;// 当前节点的编号
104         for(int i=0;i<l;i++)
105         {
106             int c=idx(s[i]);
107             while(j&&!ch[j][c])
108             j=f[j];// 顺着失配边走
109             j=ch[j][c]; // 取到儿子
110             if(val[j])
111             ok(j);// 找到一个单词
112             else if(last[j])
113             ok(last[j]);// 当前节点不行就看看上一个单词出现的位置行不行
114         }
115     }
116 }ac;
117 int n;
118 int main()
119 {
120     while(scanf("%d",&n)==1&&n!=0)
121     {
122         ac.Init();
123         for(int i=1;i<=n;i++)
124         {
125             scanf("%s",s[i]);
126             ac.Insert(s[i],i);
127         }
128         ac.getfail();
129         scanf("%s",text);
130         ac.find(text);
131         int ans=-1;
132         for(int i=1;i<=n;i++)
133             if(ac.cnt[i]>ans) ans=ac.cnt[i];
134         printf("%d\n",ans);
135         for(int i=1;i<=n;i++)
136             if(ac.cnt[i]==ans)
137                 printf("%s\n",s[i]);
138     }
139     return 0;
140 }
时间: 2024-10-15 12:57:40

P3796 【模板】AC自动机(加强版)的相关文章

算法模板——AC自动机

实现功能——输入N,M,提供一个共计N个单词的词典,然后在最后输入的M个字符串中进行多串匹配(关于AC自动机算法,此处不再赘述,详见:Aho-Corasick 多模式匹配算法.AC自动机详解.考虑到有时候字典会相当稀疏,所以引入了chi和bro指针进行优化——其原理比较类似于邻接表,这个东西本身和next数组本质上是一致的,只是chi和bro用于遍历某一节点下的子节点,next用于查询某节点下是否有需要的子节点) 1 type 2 point=^node; 3 node=record 4 ex:

[模板]AC自动机(1)

题目描述 给定一个文本串和多个模式串,求有几个模式串出现在文本串中 #include <cstdio> #include <cstring> #include <algorithm> #define MAXN 1000005 char s[MAXN]; int N; struct queue{ int que[MAXN];int head,tail; queue():head(1),tail(0){} inline void pop(){head++;} inline

模板——AC自动机

#include<bits/stdc++.h> using namespace std; struct nob{ int fail,son[27],ed; }a[1000000]; int cnt=0; void build (string s){ int now=0; for (int i=0; i<s.length(); i++){ if (a[now].son[s[i]-'a']==0) a[now].son[s[i]-'a']=++cnt; now=a[now].son[s[i]

AC自动机例题

P3808 [模板]AC自动机(简单版) [题目描述] 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. #include<bits/stdc++.h> using namespace std; typedef long long LL; const int INF=1e9+7; inline LL read(){ register LL x=0,f=1;register char c=getchar(); while(c<48||c>57){if(c=='-')f=

【题解】P3796【模板】AC自动机(加强版)

[题解]P3796 [模板]AC自动机(加强版) 记录当前\(cnt\)是第几个"星".记录第几个串是对应着第几个星. 这里补充一点对于\(AC\)自动机的理解.可能一直有个问题我没有想明白,就是打标记的点只有一个,然而匹配时,假若一个分支包括了另一个不同的分支该怎么办.实际上,我们可以在匹配的时候使用\(fail\)数组进行类似链式前向星的遍历,从而遍历到那个打标记的地方.那么问题来了,怎么保证链式前向星会遍历到那个打了标记的节点呢?答案就在\(gen\_fail\)的玄机里.\(g

luogu P3796【模板】AC自动机(加强版)

嘟嘟嘟 这个和某谷的AC自动机模板简单版差不多. 但还是要注意几点的: 1.这个是统计出现次数,而不是是否出现,所以在查询的时候加上这个节点的val后,不能把val标记为-1.那么也就可以说查询的时间复杂度能比简单版的稍微第一慢一点. 2.考虑k个一样的模式串:刚开始我想的是每一个节点开一个vector,记录这里是第几个模式串.但其实没有这个必要,对于相同的模式串,我们只用记录任意一个就行,反而在出现次数上要都加上.因为如果主串中存在这些相同的模式串,那么出现次数应该是出现次数 * k.输出的时

P3796 【模板】AC自动机(加强版) 题解(Aho-Corasick Automation)

题目链接 AC自动机 解题思路 AC自动机模板题. 刚学AC自动机,写一篇博客增强理解. AC自动机最关键的一点在于,\(fail\)失配指针的构造. \(fail\)指针指向的地方,是匹配出现错误后进行重新匹配的位置,这说明,从根开始到\(fail\)指针指向的地方这一块字符串,正是我们刚刚失配之前配上的那一块字符串(子串),且为最长子串.这一点和KMP算法相同. AC代码 #include<stdio.h> #include<string.h> int ac[100010][2

LG5357 「模板」AC自动机(二次加强版) AC自动机+fail树

问题描述 LG5357 题解 不是fail树的AC自动机复杂度是假的. 把AC自动机搞出来,建立Trie树,树上爆搜一遍就好了. \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; template <typename Tp> void read(Tp &x){ x=0;char ch=1;int fh; while(ch!='-'&&(ch>'9'||ch<'0')) c

【模板】AC自动机

来自洛谷的两道AC自动机模板题: [模板]AC自动机(简单版) 题目背景 这是一道简单的AC自动机模板题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 管理员提示:本题数据内有重复的单词,且重复单词应该计算多次,请各位注意 题目描述 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. 输入输出格式 输入格式: 第一行一个n,表示模式串个数: 下面n行每行一个模式串: 下面一行一个文本串. 输出格式: 一个数表示答案 输入输出样例 输