ac自动机

KMP,Trie,AC自动机题目集

放肆的年华 提交于 2020-04-06 08:47:44
字符串算法并不多,KMP,trie,AC自动机就是其中几个最经典的。字符串的题目灵活多变也有许多套路,需要多做题才能体会。这里收集了许多前辈的题目做个集合,方便自己回忆。 KMP题目: https://blog.csdn.net/qq_38891827/article/details/80501506 Trie树题目: https://blog.csdn.net/qq_38891827/article/details/80532462 AC自动机:模板 https://www.luogu.org/blog/42196/qiang-shi-tu-xie-ac-zi-dong-ji AC自动机题目集: https://www.cnblogs.com/kuangbin/p/3164106.html KMP: LuoguP3375 KMP模板 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1000000+10; char a[N],b[N]; int n,m,nxt[N],f[N],g[N]; void get_next() { nxt[1]=0; for (int i=2,j=0;i<=n;i++) { while (j>0 && a[j+1]!=a[i]) j

洛谷 P3808 【模板】AC自动机(简单版) 题解

狂风中的少年 提交于 2020-03-18 17:40:07
原题链接 前置知识: 字典树。(会 \(\texttt{KMP}\) 就更好) 显然呢,本题用 字典树 和 \(\texttt{KMP}\) 无法解决问题。 所以我们发明了一个东西: \(\texttt{AC}\) 自动机! 自动AC就算了吧 首先,我们给这些串建字典树。 建完之后,我们求 失配指针 。 这是干嘛的?求完再说。 它表示以 \(i\) 节点为 结尾 的串的 后缀 有最大公共长度的 前缀 的 结尾 编号. 可能有点绕,但是字符串匹配算法,一开始就是雾里云里,后来就是拨云见雾。 引用一张洛谷题解上的图吧。 然后,比方说第 \(3\) 层的 \(c\) . 首先,以它结尾的后缀有: \(\texttt{c}\) , \(\texttt{bc}\) , \(\texttt{abc}\) . 显然,从根开始的前缀(不含根)找不到 \(\texttt{abc}\) . 但是,我们找到了 \(\texttt{bc}\) . 所以,就指向了 \(\texttt{bc}\) 中的 结尾 编号的 \(c\) 的位置。 余下同理,读者自行推理。 下面,我们假设这个图的只有每个叶子节点都是一个单词的末尾。那么,假设我们要找在 \(\texttt{abcde}\) 中的次数,一开始 \(ans = 0\) .流程为: 首先一路往下,到 \(d\) 之后发现 \(e\) 没了。这时 \(ans

bzoj3940 censoring 题解(AC自动机)

断了今生、忘了曾经 提交于 2020-03-12 21:41:53
题目描述 Farmer John has purchased a subscription to Good Hooveskeeping magazine for his cows, so they have plenty of material to read while waiting around in the barn during milking sessions. Unfortunately, the latest issue contains a rather inappropriate article on how to cook the perfect steak, which FJ would rather his cows not see (clearly, the magazine is in need of better editorial oversight).FJ has taken all of the text from the magazine to create the string S of length at most 10^5 characters. He has a list of censored words t_1 ... t_N that he wishes to delete from S. To do so Farmer

学习笔记:AC自动机

五迷三道 提交于 2020-03-10 18:35:31
这里的 \(AC\) 不要理解错啊...... 让一个文本串跑好多模式串的 \(KMP\) ,全称 \(Aho-Corasick\;automaton\) 很神奇, 就像面条机一样 。 将线形的字符串改成树形的 \(Trie\) 了,将令人懵逼的 \(next[]\) 数组改为难以理解的失配指针 \(fail\) 。 引用大佬 \(yyb\) 的话: \[Trie\text{树的失配指针是指向:沿着其父节点的失配指针,一直向上,直到找到拥有当前这个字母的子节点的节点的那个子节点}\] 再盗张图吧: 于是我们可以先建一颗平常的 \(Trie\) : 这里是结构体: struct node { int kid[28];//对应的儿子节点(a-0,z-25) int end,fail;//分别是有几个子串在此终结,fail指针 }ac[MAXN]; 插入操作(大家都会的) void add(char *s) { int len=strlen(s),u=0; for(int i=0;i<len;i++) { int j=s[i]-'a'; if(!ac[u].kid[j]) ac[u].kid[j]=++cnt; u=ac[u].kid[j]; } ac[u].end++; } 接下来就是寻找每个节点的失配指针了: 思路是这样的: 对于每个节点,枚举所有可能的儿子节点(a--z)

[SDOI2014] 数数 - AC自动机,数位dp

…衆ロ難τιáo~ 提交于 2020-03-10 09:43:53
给定一个长度为 \(l\) 的大数 \(n\) ,求 \(\leq n\) 且不以子串形式包含给定 \(m\) 字典的数的个数。 \(l\leq 1200, m\leq 100, \sum len \leq 1500\) Solution 设 \(f[i][j][0/1]\) 表示考虑了前 \(i\) 位,走到 \(j\) 结点,下一个字符是否挨着上界的方案数 转移方法有 \(f[i-1][j][0] \to f[i][ch[j][0..9]][0]\) \(f[i-1][j][1] \to f[i][ch[j][0..s[i]-1]][0]\) \(f[i-1][j][1] \to f[i][ch[j][s[i]]][1]\) 但是考虑到数字中不能有前导零但是字典中可以有 据说只需要大力删除 \(ch[0][0]\) 就可以解决该问题(大雾) #include <bits/stdc++.h> using namespace std; const int N = 5005; const int mod = 1e+9 + 7; #define ch c queue <int> q; int n,m,c[N][10],f[N][N][2],val[N],fi[N],cnt,ans[1005]; void ins(char *str,int id) { int len=strlen

CF547E Mike and Friends [AC自动机,离线树状数组]

泪湿孤枕 提交于 2020-02-22 19:33:03
#include <cstdio> #include <queue> #include <vector> #define pb emplace_back using namespace std; int read() { int x = 0; char c = getchar(); while (c < 48) c = getchar(); while (c > 47) x = x * 10 + (c - 48), c = getchar(); return x; } const int maxn = 2e5 + 52; const int maxq = 5e5 + 55; int n, m, len = 0; char s[maxn]; int fa[maxn], ch[maxn][26], fail[maxn], cnt = 1; int ed[maxn]; void rs() { len = 0; char c = getchar(); while (c == ' ' || c == '\n') c = getchar(); while (c != ' ' && c != '\n') s[len++] = c, c = getchar(); } int ins() { int p = 1; for (int i = 0; i < len; i++) { int c = s[i

AC自动机+高斯消元 hdu 5955 Guessing the Dice Roll 16沈阳icpc

試著忘記壹切 提交于 2020-02-16 09:37:37
在AC自动机上,目标节点建立xi = 1的方程,非目标节点建立xi = 0 的方程,其余节点根据Trie树Fail数组转移,建立 xi = ∑ aj * x[i->j] 然后sz个方程,sz个未知数,解得x0,即为从原始状态(游戏开始)到 第i人胜出的概率。 利用高斯消元解方程x0 代码 #include <bits/stdc++.h> const long long mod = 1e9+7; const double ex = 1e-10; const int maxn = 205; #define inf 0x3f3f3f3f using namespace std; int sgn(double x){ if (x > ex) return 1; if ( x < -ex) return -1; return 0; } struct Trie{ int ch[maxn][10]; int last[maxn]; int val[maxn]; int f[maxn]; double gauss[150][150]; int vis[150]; int now; int sz; Trie() { sz = 1; memset(ch[0],0,sizeof(ch[0])); } void init(){ sz = 1; memset(ch,0,sizeof(ch)); memset

HDU - 2896 病毒侵袭(AC自动机)

試著忘記壹切 提交于 2020-02-02 15:24:35
题目链接: 点击查看 题目大意:给出 n 个模式串,再给出 m 个匹配串,问有多少个模式串在匹配串中出现,需要分别对应上其编号 题目分析:对应编号问题我们可以直接开一个数组映射,也是比较经典的模板问题了,注意输出格式就好了,还有如果MLE的话,用指针的朋友可以用C++交一下试试,用数组的朋友不要滥用memset,剩下的就是模板了 代码: #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<set> #include<sstream> #include<unordered_map> using namespace std; typedef long long LL; const int inf=0x3f3f3f3f; const int N=1e5+100; char s[N]; int fail[N],trie[N][130],cntword[N],rk[N],vis[N],cnt,tot; void insert_word(int id) { int len=strlen(s);

AC自动机总结

試著忘記壹切 提交于 2020-01-29 11:27:59
用了将近一周的时间,总算把AC自动机后面四道dp做完了 先说一下总体感受:全是套路 AC自动机的题dp一般就是第一维表长度,第二维表节点,然后从父亲转移到儿子(当然偶尔有例外) 而且做完之后发现AC自动机建trie树完全没卵用,几乎都得用到trie图(trie树会各种re) 来说一下做这个专题的经历 前三道题就是模板,没啥好说的 然后第四道题卡了一会 D:病毒 题目大意:给定几个串,问是否存在一个无限长的串不包含其中任何一个串 题解:在AC自动机上跑dfs,在不经过危险节点的情况下,看能不能跑出来环 这里包含了一个重要性质:在AC自动机上跑dfs(不加限制),能遍历所有可能的串,这将成为接下来几乎所有题的突破口 #include <queue> #include <cstdlib> #include <cmath> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> #define SI 2 using namespace std; int cnt; struct node{ node *fi; node *tr[SI]; int c,is,are; node(){ fi=NULL; is=1;are=0; c=++cnt; memset(tr

E. Tree-String Problem (AC自动机+fail树)

最后都变了- 提交于 2020-01-29 09:01:17
https://codeforces.com/problemset/problem/291/E 题意:给你一颗树,然后每一条边有一个字符串,然后给你一个字符串,问这个字符在所有的树的根到叶子结点练成的串中,出现了多少次。 做法:建立AC自动机,每个点进行计数,在对询问串建AC自动机时不需要计数,然后dfs遍历fail树进行计数就可以了。 # include "bits/stdc++.h" using namespace std ; typedef long long ll ; const int maxn = 300000 + 10 ; const ll mod = 1000000000 + 7 ; # define lowbit(x) x&-x int nxt [ maxn ] [ 26 ] , fail [ maxn ] , sum [ maxn ] , tot ; int modify ( string s , int now , int f ) { int len = s . size ( ) ; for ( int i = 0 ; i < len ; i ++ ) { int id = s [ i ] - 'a' ; if ( nxt [ now ] [ id ] == 0 ) nxt [ now ] [ id ] = ++ tot ; now = nxt [ now ]