并不对劲的AC自动机
这像是能解决所有问题的样子(并不)。AC自动机之所以叫AC自动机是因为它能解决所有AC自动机的题。
其实只能解决的是很多模式串匹配一个母串的问题。
把kmp中的next数组得到下一次跳转的位置看成特殊的边,把字符串看成链,就会得到一个特殊的图。
一个点u的next连向点v对应的字符串是u最长的后缀,把这个图套用到很多个模式串组成的trie上。
对于点u能走到的节点ch[u][i],相当于在u对应的字符串后补了一个字符i。那么点u的next连向的点v对应的字符串后面再补一个字符i就是点ch[u][i]的next。有时并没有ch[v][i],那么就要找v的next也就是更短的后缀。要是一直找不到,就只能连向0号点(空串)了。算next是一定要用bfs,不然会出现要走u的next却发现u的next还没有被算出的尴尬情况。
匹配时,如果没有ch[u][i]就要走u的next。还要注意的是,能走到u说明也能走到u的next、u的next的next、u的next的next的next等,要把这些点的值也加上。这样不会TLE吗?想必是不会的。要想知道一堆字符串中有多少个是某个串的子串,走到每个字符串结尾代表的节点时,只能统计一次。那么在统计时可以进行标记,被标记的点就不走了。又因为当一个点被标记时,它的next、它的next的next等都已经被统计过了,所以当顺着next统计的时候,发现一个被标记的点就可以停止了。自动机上的每一个点只会走到一次,所以并不会TLE。
考虑next很麻烦?当ch[u][i]==0时,令ch[u][i]=next!
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define maxn 1000010
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dwn(i,x,y) for(register int i=(x);i>=(y);i--)
#define vv ch[u][i]
using namespace std;
inline int read()
{
int x=,f=;
char ch=getchar();
while(isdigit(ch)== && ch!='-')ch=getchar();
if(ch=='-')f=-,ch=getchar();
while(isdigit(ch))x=x*+ch-'',ch=getchar();
return x*f;
}
inline void write(int x)
{
int f=;char ch[];
if(!x){puts("");return;}
if(x<){putchar('-');x=-x;}
while(x)ch[++f]=x%+'',x/=;
while(f)putchar(ch[f--]);
putchar('\n');
}
int ch[maxn][],val[maxn],fail[maxn],cnt=,n,len;
char s[maxn];
int gx(char c){return c-'a';}
void build()
{
int u=;
rep(i,,len-)
{
if(!ch[u][gx(s[i])])ch[u][gx(s[i])]=++cnt;
u=ch[u][gx(s[i])];
}
val[u]++;
}
void getfail()
{
fail[]=;
queue<int >q;
rep(i,,)
if(ch[][i])
q.push(ch[][i]),
fail[ch[][i]]=;
while(!q.empty())
{
int u=q.front();q.pop();
rep(i,,)
{
if(vv)
q.push(vv),fail[vv]=ch[fail[u]][i];
else vv=ch[fail[u]][i];
}
}
}
int getans()
{
int ans=,u=;
rep(i,,len-)
{
u=ch[u][gx(s[i])];
for(int j=u;j&&val[j]!=-;j=fail[j])
ans+=val[j],val[j]=-;
}
return ans;
}
int main()
{
n=read();
rep(i,,n)
scanf("%s",s),len=strlen(s),build();
getfail();
scanf("%s",s),len=strlen(s);write(getans());
return ;
}
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<string>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define LL long long
#define MAXN 1000+1000000
#define INF 2147483647 using namespace std;
inline int read(){
int x=,f=; char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=ch-''+(x<<)+(x<<);
return x*f;
} char str[MAXN];
struct ACAutomata{
int ch[MAXN][],val[MAXN],size;
queue<int>Q; int fail[MAXN],vis[MAXN];
void insert(){
int cur=,len=strlen(str);
for(int i=;i<len;i++){
if(ch[cur][str[i]-'a'])cur=ch[cur][str[i]-'a'];
else ch[cur][str[i]-'a']=++size,cur=size;
}
val[cur]++; return ;
} void calc(){
while(!Q.empty()){
int u=Q.front(); Q.pop();
for(int i=;i<;i++){
if(!ch[u][i])continue;
int cur=fail[u]; Q.push(ch[u][i]);
while(!ch[cur][i]&&cur)cur=fail[cur];
fail[ch[u][i]]=ch[cur][i];
}
}
return ;
} int solve(){
int cur=,ans=,len=strlen(str),pos=;
while(pos<len){
for(int i=cur;!vis[i]&&i!=;i=fail[i])
ans+=val[i],vis[i]=;
if(ch[cur][str[pos]-'a'])cur=ch[cur][str[pos]-'a'];
else {
cur=fail[cur];
while(!ch[cur][str[pos]-'a']&&cur)cur=fail[cur];
cur=ch[cur][str[pos]-'a'];
}
for(int i=cur;!vis[i]&&i!=;i=fail[i])
ans+=val[i],vis[i]=; pos++;
}
return ans;
}
}T; int main(){
int n=read();
for(int i=;i<=n;i++){scanf("%s",str);T.insert();}
for(int i=;i<=;i++)if(T.ch[][i])T.Q.push(T.ch[][i]);
T.calc(); scanf("%s",str); printf("%d",T.solve());
return ;
}
别人一般把这种并不对劲的next称为失配边、fail之类的。
刚刚说过这是一个特殊的图,因为每个点只会连出一条失配边,所有失配边也组成了一棵树。
这样树上的某些坑人操作也可以在AC自动机上做了。。。
并不对劲的AC自动机的更多相关文章
- 基于trie树做一个ac自动机
基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
- python爬虫学习(11) —— 也写个AC自动机
0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3198 Solved: 1532[Submit][Status ...
- BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]
1212: [HNOI2004]L语言 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1367 Solved: 598[Submit][Status ...
- [AC自动机]【学习笔记】
Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...
- AC自动机 HDU 3065
大概就是裸的AC自动机了 #include<stdio.h> #include<algorithm> #include<string.h> #include< ...
- AC自动机 HDU 2896
n个字串 m个母串 字串在母串中出现几次 #include<stdio.h> #include<algorithm> #include<string.h> #inc ...
随机推荐
- DP在字符匹配上的实现
在此保存下近段时间做的DP在字符匹配上的实现的题目 对于不同的字符串来说,2者只能不断将下标往后推移来实现匹配从而得到的最大匹配数 如 abcd 和 dcba 这个最大匹配数只能为1,因为两个d匹配后 ...
- 指针,数组,字符串的区别(高质量程序设计指南C++/C语言第7章)
指针: 指针是变量,和平时的那些变量没有本质的差异,不同的只是它的值和类型,.,即解释方式 二进制层面:指针的值是内存单元的地址,而变量是引用内存单元值的别名 语言层面:指针的值就是变量的地址. 对象 ...
- “亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 A noj 2073 FFF [ 二分图最大权匹配 || 最大费用最大流 ]
传送门 FFF 时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte总提交 : 145 测试通过 : 13 ...
- tomcat并发数
Tomcat的最大并发数是可以配置的,实际运用中,最大并发数与硬件性能和CPU数量都有很大关系的.更好的硬件,更多的处理器都会使Tomcat支持更多的并发. Tomcat默认的HTTP实现是采用阻塞式 ...
- (二)Commonjs规范与模块化
在之前的学习中我们使用require()来引入我们需要的包,这其实就是模块化,各模块相互独立,可以通过某种方式引入别的模块.而这些引入方式都是遵循一定的规范的,这就是CommonJS规范. 一.Com ...
- UVA 3882【dp】【简单数学】
题意: 给定三个数分别是: 人数 间隔 起点 题目中人的编号从1开始.在进行约瑟夫环的判定之后,求解最后能够活下来的人. 思路: 约瑟夫环的递推公式是 f[n]=(f[n-1]+jian ...
- Java子类重写父类方法注意问题收集(转)
子类不能重写父类的静态方法,私有方法.即使你看到子类中存在貌似是重写的父类的静态方法或者私有方法,编译是没有问题的,但那其实是你重新又定义的方法,不是重写.具体有关重写父类方法的规则如下: 重写规则之 ...
- 我被C++开发欺辱的岁月
前言 人被压迫了,为什么不斗争?——鲁迅 作为一个C#开发者,我经历了,也见证了很多同行饱受C++开发的歧视和欺辱. 而且,这种行为,现在依然持续的发生在C#开发者的身上,就目前为止,绝大部分C#开发 ...
- 247. Segment Tree Query II
最后更新 二刷 09-Jna-2017 利用线段树进行区间查找,重点还是如何判断每一层的覆盖区间,和覆盖去见与当前NODE值域的关系. public class Solution { public i ...
- Hadoop架构设计、执行原理具体解释
1.Map-Reduce的逻辑过程 如果我们须要处理一批有关天气的数据.其格式例如以下: 依照ASCII码存储.每行一条记录 每一行字符从0開始计数,第15个到第18个字符为年 第25个到第29个字符 ...