ac自动机

【模板】 ac自动机

最后都变了- 提交于 2019-11-28 04:16:17
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #define maxn 5000000+10 6 using namespace std; 7 char str[maxn*2]; 8 struct node{ 9 int fail;//失配指针; 10 int cnt;//单词出现的次数; 11 int next[62];// 此节点的下一个(儿子)节点; 12 }trie[maxn];//节点结构体; 13 int k=0,ans=0; 14 queue<int> q;//队列:建失配指针使用; 15 void build_trie(int id,char *s)//id表示第几个结点,即所有字符串从第0个节点开始向下建;*s即表示这个字符串; 16 { 17 int len=strlen(s);//该字符串的长度;(相当于字符串最后一个字符的深度) 18 int j=0; 19 for(int i=0;i<len;i++){ 20 j=s[i]-'a'; 21 if(trie[id].next[j]==0)/*若此字母未出现在当前位置的下一深度*/ 22 { 23 trie[id].next[j]=++k;//当前节点对于j字母节点的位置;即j字母的节点序号;

Separate String(Ac自动机+dp)

一个人想着一个人 提交于 2019-11-28 01:50:28
题意 给出大小为 \(n\) 的字符串集合,给定字符串 \(t\) ,求拆分 \(t\) 的方案数,要求串 \(t\) 拆分后每一个串都要是集合中的某个串。 答案取模1e9+7 思路 对于一个串 \(t\) 的第i个位置,如果他是某个串的结尾, 并且这个串之前的串也是个合法串,那么可进行dp转移,可用ac自动机的fail指针来维护第 \(i\) 个位置是否为某个串的结尾并遍历集合中所有合法的串。 Code #include <bits/stdc++.h> using namespace std; const int maxn = 2e5+10; const int mod = 1e9+7; int dp[maxn], pos[maxn]; struct Ac { int tr[maxn][26], fail[maxn*26], e[maxn*26], len[maxn*26]; int dep[maxn*26]; int tot; 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; dep[tot] = dep[p]+1; } p = tr[p][c]; } e[p]=1; len[p] = dep[p]; }

BCD Code ZOJ - 3494 AC自动机+数位DP

我与影子孤独终老i 提交于 2019-11-27 22:03:05
题意: 问A到B之间的所有整数,转换成BCD Code后, 有多少个不包含属于给定病毒串集合的子串,A,B <=10^200,病毒串总长度<= 2000. BCD码这个在数字电路课上讲了,题干也讲的很详细。 数位DP的实现是通过0~9 ,并不是通过BCD码 所有我们需要先把字串放入AC自动机,建立一个BCD数组 因为BCD码是一个4位二进制数,但是tire图上全是0,1, 所以对于一个数字,我们的要在转移4次, 如果中间出现了病毒串就return -1 表示不能转移, BCD【i】【j】表示在AC自动机 i 这个节点转移到数字 j 对应的在AC自动机上的节点标号。 然后就是简单的数位DP了,然而我写搓了,由于没有前导0所以前导0要处理掉。 但是你不转移的时候,不能 bcd[idx][i] != -1 就直接continue , 因为有0的情况,i==0 但是(bcd[idx][i] == -1) 但是这个0是前导0所以不影响。 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <ctime> 7 #include <cstdio> 8 #include <string> 9 #include <vector> 10

Lost's revenge HDU - 3341 AC自动机+DP(需要学会如何优雅的压缩状态)

佐手、 提交于 2019-11-27 21:57:08
题意: 给你n个子串和一个母串,让你重排母串最多能得到多少个子串出现在重排后的母串中。 首先第一步肯定是获取母串中每个字母出现的次数,只有A T C G四种。 这个很容易想到一个dp状态dp【i】【A】【B】【C】【D】 表示在AC自动机 i 这个节点上,用了A个A,B个T,C个C,D个G。 然后我算了一下内存,根本开不下这么大的内存。 看了网上题解,然后用通过状压把,A,B,C,D压缩成一维。 这个状压就是通过进制实现需要实现唯一表示 bit[ 0] = 1; bit[ 1] = (num[ 0] + 1); bit[ 2] = (num[ 0] + 1) * (num[ 1] + 1); bit[ 3] = (num[ 0] + 1) * (num[ 1] + 1) * (num[ 2] + 1); 这样就实现了A,B,C,D的唯一表示。 知道如何优化空间这题就变得非常简单了。 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <ctime> 7 #include <cstdio> 8 #include <string> 9 #include <vector> 10 #include <cstring> 11 #include

Ring HDU - 2296 AC自动机+简单DP和恶心的方案输出

跟風遠走 提交于 2019-11-27 21:52:27
题意: 就是现在给出m个串,每个串都有一个权值,现在你要找到一个长度不超过n的字符串, 其中之前的m个串每出现一次就算一次那个字符串的权值, 求能找到的最大权值的字符串,如果存在多个解,输出最短的字典序最小的串。 当最大全权值为0时输出空串。 输入最多100个子串,权值为不超过100的正整数。 每个子串长度至少为1,不超过10, n <= 50 如果不考虑方案输出,这题就变得相当简单了。 dp【i】【j】表示走到长度为 i 的时候 ,到AC自动机 j 这个节点所获得的最大权值和。 我一开始的做法是在dp的过程中获得那个最优方案。 最后死活过不去,就换了一种超级暴力的写法。 将所有情况保存下来,去一个个找字典序最小的方案。 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <cstdio> 7 #include <string> 8 #include <vector> 9 #include <time.h> 10 #include <cstring> 11 #include <iostream> 12 #include <algorithm> 13 14 #define pi acos(-1.0) 15 #define eps 1e

HDU - 2896 病毒侵袭 (AC自动机,last优化)

吃可爱长大的小学妹 提交于 2019-11-27 20:52:07
题目链接: HDU - 2896 题意:给你n个模式串,对应每个模式串由编号,给出m个文本串,然后你要输出对应所匹配出模式串序号,以及有多少个文本串中有模式串 思路:比起比较基本统计个数,这里我们可以用set或者map来统计模式串序号 这里总结一下新学习的last优化。 参考博客: (1) last相当于一个超级fail指针: 因为我们只有到根节点时才会重新匹配一个字母,所以我们此时直接记录一个last ,直接结束当前匹配过程.直接省去原 Fail 指针到可以匹配的节点之间的距离. 同时结合路径压缩,在匹配时可以完全不使用原 Fail.可以看下参考博客里面的图片。 code: 详细注解 #include<bits/stdc++.h> const int N=100000+5; using namespace std; struct AC_automaton{ int trie[N][128];//字典树 int val[N];//字符串结尾标记 int fail[N];//失配指针 int last[N];//last[i]=j表j节点表示的单词是i节点单词的后缀,且j节点是单词节点(last优化) int tot;//编号 void init(){//初始化0号点 tot=1; val[0]=fail[0]=last[0]=0; memset(trie[0],0,sizeof

洛谷P3796 AC自动机

杀马特。学长 韩版系。学妹 提交于 2019-11-27 19:18:15
AC自动机模板 #include <bits/stdc++.h> #define INF 0x3f3f3f3f #define full(a, b) memset(a, b, sizeof a) #define __fastIn ios::sync_with_stdio(false), cin.tie(0) #define pb push_back using namespace std; using LL = long long; inline int lowbit(int x){ return x & (-x); } inline int read(){ int ret = 0, w = 0; char ch = 0; while(!isdigit(ch)){ w |= ch == '-', ch = getchar(); } while(isdigit(ch)){ ret = (ret << 3) + (ret << 1) + (ch ^ 48); ch = getchar(); } return w ? -ret : ret; } template <typename A> inline A __lcm(A a, A b){ return a / __gcd(a, b) * b; } template <typename A, typename B, typename C>

【AC自动机】文本生成器

三世轮回 提交于 2019-11-27 14:14:13
【题目链接】 https://loj.ac/problem/10063 【题意】 给出长度为m,n个模式串,请问只要长度为m的串中有一个模式串就算是可读。 【分析】 其实如果直接分析全部可读的情况,一个串,两个串,……n个串可读。 明显是很复杂而且是做不出来的。 正难则反,其实我们可以需要通过一个(所有可能-全部不可读的情况)不就是答案的方案吗? 记录不合法的方案,考虑dp[i][j] ,下标为j,长度为i 的不可读的情况。 其实不可读的情况就是不经过结尾的结点即可。 最后答案就是利用26个字母,然后子节点通过父节点来累加答案。 最后把对立事件算出来即可。 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 6 using namespace std; 7 const int N = 10005; 8 const int M = 2e6+10; 9 const int mod = 10007; 10 int Trie[M][26] , fail[M] , End[M], idx ; 11 int Q[M] , Head , Tail ; 12 int f[N][N]; 13 int n ,m ; 14 15 char str[N]; 16 17 void Insert

【AC自动机】病毒

元气小坏坏 提交于 2019-11-27 14:13:35
【题目链接】 https://loj.ac/problem/10062 【题意】 寻找一个没有模式串为子串的无限01串。是否存在。 【题解】 其实就是用dfs找一个环。 1、环需要从根结点出发找到这个位置。且重新能走到这里,我们开一个“预测路径”的数组进行标记即可。 2、如果下一个结点碰上了“走过”标记过的,而不是“预测路径”,或者是模式串结尾,我们就避开不走。 注意: fail数组是可以进行传递的,就是说fail指向的结点,同时是模式串的结尾,那么该位置也是不合法的。 最后看看代码怎么实现就明白了。 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 5 using namespace std; 6 7 const int N = 2005; 8 const int M = 30005; 9 10 int Trie[M][2] , fail[M] , End[M], idx ; 11 int Q[M] , Head , Tail ; 12 int vis[M],used[M]; 13 int n ; 14 15 char str[N]; 16 17 void Insert( char s[] ){ 18 int p = 0; 19 for(int i=0 ; s[i] ; i++ ){ 20 int t =

【AC自动机】最短母串

不打扰是莪最后的温柔 提交于 2019-11-27 14:13:23
【题目链接】 https://loj.ac/problem/10061 【题意】 给定 n 个字符串 S1~Sn ,要求找到一个最短的字符串 T ,使得这 n 个字符串都是 T 的子串。 【题解】 类似于搜索+二进制记录状态的题目 搜索时利用BFS来跑,每一个结点的位置都可以用状态数组存起来, 判断是否为 (1<<n)- 1 即可。 在输出答案时需要递归实现,所以要一个辅助数组fa来记录上一个结点的位置。 1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N = 6e3+5; 7 const int M = 2e6+50; 8 const int Str_N = 605; 9 int Trie[N][26],fail[N],End[N]; 10 int vis[N][Str_N]; 11 int Q[M],St[M]; 12 int Fa[M]; 13 char str[M]; 14 int Head,Tail; 15 int n,idx; 16 char Str[Str_N]; 17 void print(int x){ 18 if(x==1) return ; 19 print(Fa[x]); 20