ac自动机

AC自动机

混江龙づ霸主 提交于 2019-11-30 07:10:58
AC自动机 1.常见的就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。 2.算法分为3步:构造一棵Trie树,构造失败指针和模式匹配过程。简单来说,AC自动机是用来进行多模式匹配(单个主串,多个模式串)的高效算法 题目: Keywords Search In the modern time, Search engine came into the life of everybody like Google, Baidu, etc. Wiskey also wants to bring this feature to his image retrieval system. Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched. To simplify the problem, giving you a description of image, and some keywords, you should

比较全的字符串算法汇总

断了今生、忘了曾经 提交于 2019-11-30 02:13:43
目录 KMP AC自动机 AC自动机_引入 AC自动机的构建 AC自动机查找 模板代码 注意事项 例题选讲 另一种写法 hash&&trie&&manacher SA后缀数组 1、后缀数组作用 2、后缀数组的构造 3、 SA算法的用途 4、例题:poj 3261 : Milk Patterns 后缀树 (suffix-tree) 后缀自动机(SAM) 大坑填完了! KMP 做题需要脑筋急转弯 板子 for(int i=2;i<=n;i++){ int j=next[i-1]; for(;j&&s[j+1]!=s[i]) j=next[j]; if(s[j+1]==s[i]) next[i]=j+1; else next[i]=0; } 匹配: int i=0,j=0; while(i<=len1){ if(j==0||s1[i+1]==s2[j+1]){ i++,j++; }else j=next[j]; if(j==len2){ printf("%d\n",i-len2+1); j=next[j]; } } AC自动机 AC自动机_引入 对于k个模式串,我们要匹配一个文本串。 如果采用建立k个next数组的方法( kmp ),时间复杂度显然为O((m i +n)*k),不可接受。 那我们就需要一种更简便的数据结构(误),来实现在可控时间范围内(O(n))内的匹配。 除此之外

AC自动机总结

孤街醉人 提交于 2019-11-29 19:47:14
AC自动机总结 AC自动机简述 功能 多模板串对单个或多个串的匹配问题 主体思想 原理同 \(kmp\) , 在 \(trie\) 树上使用变种的 \(kmp\) 实现 需要数组 : \(trie[N][26],fail[N]\) \(fail\) 即我们所说的失配函数, \(trie[]\) 则略有变更 准确一点得说, \(fail\) 函数是不需要知道后继字母的失配 而 \(trie\) 树上的节点经过处理后,就可以直接 \(O(1)\) 访问后继每一种字母的情况的下一个位置,不需要再一次次失配 预处理 AC自动机的预处理与 \(kmp\) 的预处理只有一点不同 \(kmp\) 的 失配数组 \(nxt[N]\) , 是在不知道下一位的字母,一次次失配直到与下一位匹配 int j=0; for(int i=1;i<=n;++i){ while(j && s[i]!=t[j+1]) j=nxt[j]; if(s[i]==t[j+1]) j++; } 然而AC自动机既然已经基于 \(trie\) 树结构,自然可以对于每个下一位字母的情况来匹配,这里我们分类讨论 ​ 如果已经存在同字母的节点,那么就是下一位节点,而它的 \(fail\) 就是上一个失配位置 \(fail\) 的这一个字母的后继节点 ​ 如果不存在,就是上一个失配位置 \(fail\) 的这一个字母的后继节点 (

【题解】GREWords(AC自动机)

时间秒杀一切 提交于 2019-11-29 10:10:10
【题解】GREWords(AC自动机) SP9941 GRE - GRE Words 题目大意: 给定一个由字符串构成的序列,不同位置的字符串有自己权值。现在让你选出一个子序列,使得在这个子序列中,前面的串是后面的串的子串。请你求满足条件的子序列的权值的最大值。一个子序列权值是所有元素权值的和。 考虑一个朴素的DP做法,设 \(dp(i)\) 表示选择了字符串 \(i\) 的最大权值子序列,转移直接枚举前面的字符串,通过 \(kmp\) 判断是否为子串,然后转移,复杂度 \(O(n^3)\) 考虑优化一下上面的做法,众所周知,ac自动机 \(fail\) 树上任意节点到根一条链是一段后缀。那么,一个字符串的所有子串就是它在ac自动机上的 所有节点 的所有到根链。(子串=自己某段前缀的后缀) 要优化上面那个 \(dp\) ,就是要求我们维护一个数据结构,使得可以根据一个字符串快速查询已有的他所有的子串的权值的最大值。考虑这样一种做法,把 \(fail\) 树单独拿出来,现在假设我有个 \(dp\) 值更新就只会对他的子树有影响,考虑到子树的一段连续的 \(dfs\) 序列,所以直接线段树维护一个区间取 \(max\) 单点求 \(max\) 即可。 可以直接先把fail建出来再依次更新 \(dp\) ,使得fail树的形态确定。 总复杂度 \(O(n\log n)\) //

AC自动机 POI2000病毒

独自空忆成欢 提交于 2019-11-28 22:21:19
题目链接: https://www.luogu.org/problem/P2444 题意:给你一些字符串,问能不能找到一个无限长的字符串,使得给定的这些字符串不会出现在该无限长字符串中 一般我们写ac自动机都是尽可能的使多匹配,而本题反其道而行,要尽可能的不匹配,那么我们可以遇到fail标记就跳(因为一个字符串的标记是在最后,中途就调走了肯定就不会遇到了)。如果存在一个无限长的字符串,那么我们内部肯定会形成一个环,且这个环中不会有带结束标记的点,且这个环一定要包含根节点。 PS:这题数据量非常小,我自己想过数据量大了可以结合拓扑排序,但入度不是指向fail边的起点,而是终点,这样可以从小的更新大的,但最后拓扑排序fake了,我做不出来,只能用题解方法了。 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=30007; const int inf=0x3f3f3f3f; const int N=1e7; const ll mod=998244353; #define meminf(a) memset(a,0x3f,sizeof(a)) #define mem0(a) memset(a,0,sizeof(a)) char a[maxn]; struct node{ int

TZOJ 5986 玄武密码(AC自动机)

≯℡__Kan透↙ 提交于 2019-11-28 18:57:01
描述 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。 很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。 经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为N的序列来描述,序列中的元素分别是‘E’,‘S’,‘W’,‘N’,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的M段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。 现在,考古工作者遇到了一个难题。对于每一段文字,其前缀在母串上的最大匹配长度是多少呢? 输入 第一行有两个整数,N和M,分别表示母串的长度和文字段的个数。 第二行是一个长度为N的字符串,所有字符都满足是E,S,W和N中的一个。 之后M行,每行有一个字符串,描述了一段带有玄武密码的文字。依然满足,所有字符都满足是E,S,W和N中的一个。 对于100%的数据,N<=10^7,M<=10^5,每一段文字的长度<=100。 输出 输出有M行,对应M段文字。 每一行输出一个数,表示这一段文字的前缀与母串的最大匹配串长度。 样例输入 7 3 SNNSSNS

hdu2222 Keywords Search (AC自动机板子

偶尔善良 提交于 2019-11-28 17:56:02
https://vjudge.net/problem/HDU-2222 题意:给几个模式串和一个文本串,问文本串中包含几个模式串。 思路:板子不解释。 #include<cstdio> #include<cstring> #include<queue> using namespace std; const int N=26; const int MAXN=500005; struct Trie{ int next[MAXN][N],fail[MAXN],end[MAXN]; int root; int tot; int newnode() { for(int i=0;i<N;i++) next[tot][i]=-1; end[tot++]=0; return tot-1; } void init() { tot=0; root=newnode(); } void insert(char buf[]) { int len=strlen(buf); int now=root; for(int i=0;i<len;i++) { int k=buf[i]-'a'; if(next[now][k]==-1) next[now][k]=newnode(); now=next[now][k]; } end[now]++; } void build() { queue<int> que; fail

[算法模版]AC自动机

邮差的信 提交于 2019-11-28 12:22:29
[算法模版]AC自动机 基础内容 板子不再赘述, OI-WIKI 有详细讲解。 \(query\) 函数则是遍历文本串的所有位置,在文本串的每个位置都沿着 \(fail\) 跳到根,将沿途所有元素答案++。意义在于累计所有以当前字符为结尾的所有模式串的答案。看代码就能很容易的理解。 另外 \(e[i]\) 记录的是第 \(t\) 个模式串结尾是哪个节点(所有节点均有唯一的编号)。 贴个 P5357 【模板】AC自动机(二次加强版) 板子: #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> #define maxn (int)(2e6+10000) int ch[(int)(2e5+1000)][30],fail[maxn],cnt,e[maxn],nex[maxn],n,queue[maxn],ans[maxn]; using namespace std; char s[(int)(2e6+1)]; char data[maxn]; void init() { memset(ch,0,sizeof(ch)); memset(fail,0,sizeof(fail)); memset(e,0,sizeof(e));

AC自动机模板QWQ

微笑、不失礼 提交于 2019-11-28 09:44:32
AC自动机!思想大致是先建一个trie ,再利用KMP进行计算;#include<bits/stdc++.h> #define N 5000100 using namespace std; queue<int>q; struct A_C{ int c[N][27],val[N],fail[N],cnt; void ins(char *s){ int len=strlen(s);int now=0; for(int i=0;i<len;i++){ int v=s[i]-'a'; if(!c[now][v])c[now][v]=++cnt; now=c[now][v]; } val[now]++; } void build(){ for(int i=0;i<26;i++)if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<26;i++) if(c[u][i])fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]); else c[u][i]=c[fail[u]][i]; } } int query(char*s){ int len=strlen(s); int now=0,ans=0; for(int i

hdu2222 【AC自动机】Keywords Search

心已入冬 提交于 2019-11-28 06:30:45
题意 给定n个模式串,求目标串中出现了多少个模式串。 传送门 思路 AC自动机模版题。 Code #include <bits/stdc++.h> using namespace std; const int maxn = 1e6+10; struct Ac { int tr[maxn][26], fail[maxn], e[maxn], cnt[maxn]; int tot; void init() { memset(tr, 0, sizeof(tr)); memset(e, 0, sizeof(e)); memset(cnt, 0, sizeof(cnt)); memset(fail, 0, sizeof(fail)); tot=0; } void insert(char *t) { int p=0; for (int c, i=0; t[i]; ++i) { c = t[i]-'a'; if(!tr[p][c]) tr[p][c] = ++tot; p=tr[p][c]; } ++e[p]; } void build() { queue<int>q; 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