指针-AC自动机
大家都不喜欢指针,但是这个AC自动机仿佛不用不行……
先引用我最喜欢的话:“AC自动机,不是自动AC的机器。” 如果写不好还可能一直WA
AC自动机是KMP与Trie树的完美结合,适用于多字符串匹配,这与KMP的单串不同
首先是一棵Trie树

记得建空节点做根,有大神忘记了建,直想砸键盘。
然后标准建树
如果用指针的话申请空间冷静一点
下一步就是构建fail指针,虽然我喜欢把它叫next,与KMP一致
幸好找到了好的版子,不然就很难搞
因为fail指针的构建顺序必须由上到下
即:fail是从下面的节点指向上面的节点的,这样不会漏配
So用BFS会很棒。
左面就是一个手画的树
void build(){
root->next=NULL;//根节点无fail
push(root);//根节点入队
while(!empty()){//BFS开始
TREE *i=front();//提取队首元素
for(int j=;j<;j++){//对每一个字母进行处理,尝试搜索
if(i->s[j]!=NULL){//搜到
if(i==root)i->s[j]->next=root;//是和根节点直接连的回根
else
{
TREE* p=i->next;//和搜索的点的fail指针的下节点找
while(p!=NULL)//继续找,所有fail都符合要求
{
if(p->s[j]!=NULL)//fail指针处有和j字母一样的点
{
i->s[j]->next=p->s[j];//更新结束
break;
}
p=p->next;//fail的fail也符合要求
}
if(p==NULL) i->s[j]->next=root;//无匹配,回根
}
q[++e]=i->s[j];//结果入队
}
}
}
}
经过一番建立,我们将所有的fail构建完成~~
长这样:
最好自己再画一个图,这样会更好的理解
然后就要搜索了,搜索和建立很像
补下:搜索的解释,上次忘写了,自己很吃亏,尴尬
void ffind(){//搜索函数
int j=;//从0号位开始搜
TREE *p=root;//另一方面树上从根节点走起
while(st[j])
{
int i=st[j]-'a';//不解释
while(p->s[i]==NULL && p!=root) p=p->next;//没找着,就顺着fail往下走
p=p->s[i];//这时只可能有两种可能,一是真没有,二是找着一个
if(p==NULL)p=root;//没有我们直接回根,相当于重置树上指针
TREE *k=p;//这个是找着后用的
while(k!=root && k->endt!=-)//去找
{
ans+=k->endt;//有结尾标记,这个是根据KeywordSearch的题意
k->endt=-;//为了防止重复计算 ,这个也是题意
k=k->next;//到所有的fail指针处找
}
j++;
}
}
如果是搜是否匹配或出现次数,在结尾写cnt计数即可
但如果要算匹配长度,我们就要打标记,再dfs一遍,像玄武密码一样
下面补上题和码
[模板]Keyword Search
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define L 1010101
using namespace std;
struct TREE{
TREE *next,*s[];
char dat;
int endt;
TREE(){
next=NULL;
memset(s,,sizeof s);
dat=endt=;
}
};
TREE *q[];
int f=,e=;
TREE *root;
int n,ans=;
char ch[L],st[L];
void add(char *now){
TREE *i=root;
int j=;
while(now[j]){
int k=now[j]-'a';
if(i->s[k]==NULL)
i->s[k]=new TREE();
i=i->s[k];
j++;//cout<<j<<endl;getchar();puts("P");
}
i->endt++;
}
bool empty(){
if(e==f){
return true;
}
return false;
}
void push(TREE *i){
e++;
q[e]=i;
}
TREE* front(){
if(empty())return NULL;
f++;
return q[f];
}
void build(){
root->next=NULL;
push(root);
while(!empty()){
TREE *i=front();
for(int j=;j<;j++){
if(i->s[j]!=NULL){
if(i==root) i->s[j]->next=root;
else
{
TREE* p=i->next;
while(p!=NULL)
{
if(p->s[j]!=NULL)
{
i->s[j]->next=p->s[j];
break;
}
p=p->next;
}
if(p==NULL) i->s[j]->next=root;
}
q[++e]=i->s[j];
}
}
}
}
void ffind(){
int j=;
TREE *p=root;
while(st[j])
{
int i=st[j]-'a';
while(p->s[i]==NULL && p!=root) p=p->next;
p=p->s[i];
if(p==NULL)p=root;
TREE *k=p;
while(k!=root && k->endt!=-)
{
ans+=k->endt;
k->endt=-;
k=k->next;
}
j++;
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
root=new TREE();
ans=;
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",ch);
add(ch);
}
scanf("%s",st);
build();
ffind();
printf("%d\n",ans);
}
return ;
}
[稍有思考][还是模板]玄武密码
#include <iostream>
#include <cstdio>
#include <cstring>
#define L 111111111
#define N 111111
using namespace std;
struct ACauto{
ACauto *next,*s[];
int endp,DeepinC;
bool mark;
ACauto(){
next=NULL;
memset(s,,sizeof s);
mark=DeepinC=endp=;
}
}*root;
struct Myqueue{
ACauto *Q[];int f,e;
bool empty(){
if(e==f)return true;
return false;
}
void push(ACauto *k){
e++;
Q[e]=k;
}
ACauto* front(){
if(empty())return NULL;
f++;
return Q[f];
}
}q;
int n,ans=,cnt=;
char st[L],ch[N][];
inline int turn(char ch){
switch(ch){
case 'E':return ;
case 'W':return ;
case 'S':return ;
default :return ;
}
}
void add(char *c){
ACauto *i=root;
int j=;
while(c[j]){
int k=turn(c[j]);
if(i->s[k]==NULL)
i->s[k]=new ACauto();
i=i->s[k];
i->DeepinC=j+;
// cout<<c[j]<<" "<<i->DeepinC<<endl;
j++;
}
i->endp++;
}
void build(){
root->next=NULL;
q.push(root);
while(!q.empty()){
ACauto *i=q.front();
for(int j=;j<;j++){
if(i->s[j]!=NULL){
if(i==root) i->s[j]->next=root;
else{
ACauto *p=i->next;
while(p!=NULL){
if(p->s[j]!=NULL){
i->s[j]->next=p->s[j];
break;
}
p=p->next;
}
if(p==NULL) i->s[j]->next=root;
}
q.push(i->s[j]);
}
}
}
}
void ffind(char *ser){
int j=;
ACauto *p=root;
while(ser[j]){
int i=turn(ser[j]);
while(p->s[i]==NULL&&p!=root){
p=p->next;
p->mark=;//cout<<p<<" INP "<<j<<" "<<ser[j]<<" Depth: "<<p->mark<<" "<<p->DeepinC<<endl;
}
p=p->s[i];
if(p==NULL)p=root;
ACauto *k=p;
while(k!=root && k->endp!=-){
k->mark=;
k->endp=-;
k=k->next;//cout<<k<<" INK "<<j<<" "<<ser[j]<<" Depth: "<<k->mark<<" "<<k->DeepinC<<endl;
}
// cout<<k<<" OUTK "<<j<<" "<<ser[j]<<" Depth: "<<k->mark<<" "<<k->DeepinC<<endl;
j++;
}
}
void ser(char *c){
ACauto *i=root;
int j=;
int lsans=;
bool la=;
while(c[j]){
int k=turn(c[j]);
i=i->s[k];
if(i->mark)ans=max(i->DeepinC,ans);
j++;
}
}
int main(){
int len;
root=new ACauto();
scanf("%d%d",&len,&n);
scanf("%s",st);
for(int i=;i<=n;i++) {
scanf("%s",ch[i]);
add(ch[i]);
}
build();
ffind(st);
for(int i=;i<=n;i++){
ans=;
ser(ch[i]);
printf("%d\n",ans);
}
return ;
}
指针-AC自动机的更多相关文章
- 初学AC自动机
前言 一直听说\(AC\)自动机是一个很难很难的算法,而且它不在\(NOIP\)提高组范围内(这才是关键),所以我一直没去学. 最近被一些字符串题坑得太惨,于是下定决心去学\(AC\)自动机. 简介 ...
- AC自动机 - 关于Fail指针
fail指针可以说是AC自动机里最难理解的东西,怎样更好的理解AC自动机的fail指针? 先来看一幅图: 看这幅图上的fail指针是怎么构造的. 树上的词分别是: { he , hers , his ...
- AC自动机总结及板子(不带指针)
蒟蒻最近想学个AC自动机简直被网上的板子搞疯了,随便点开一个都是带指针的,然而平时用到指针的时候并不多,看到这些代码也完全是看不懂的状态.只好在大概理解后自己脑补(yy)了一下AC自动机的代码,居然还 ...
- hdu 1277 AC自动机入门(指针版和数组版)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1277 推荐一篇博客(看思路就可以,实现用的是java): https://www.cnblogs.co ...
- bzoj 2434 AC自动机 + fail指针建树 + 树状数组
思路:我们先跟着它给定的字符串走把字典树建出来,求出fail指针,我们考虑两个字符串 A和B, 如果想要求B中有多少A的子串,转换一下就是有多少个B的前缀的后缀包含A,这个在AC自动机 的状态图中很容 ...
- CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)
What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...
- ac自动机fail树上按询问建立上跳指针——cf963D
解法看着吓人,其实就是为了优化ac自动机上暴力跳fail指针.. 另外这题对于复杂度的分析很有学习价值 /* 给定一个母串s,再给定n个询问(k,m) 对于每个询问,求出长度最小的t,使t是s的子串, ...
- POJ 1625 Censored!(AC自动机->指针版+DP+大数)题解
题目:给你n个字母,p个模式串,要你写一个长度为m的串,要求这个串不能包含模式串,问你这样的串最多能写几个 思路:dp+AC自动机应该能看出来,万万没想到这题还要加大数...orz 状态转移方程dp[ ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
随机推荐
- A - Mike and Fax
Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Description While ...
- Windows下findstr命令的使用
命令:findstr 参数解释 /b 如果位于行的开头则匹配模式. /e 如果位于行的末尾则匹配模式. /l 使用文字搜索字符串. /r ...
- vim中编辑了代码 但是提示can not write的解决办法和代码对齐办法
方式1: 1 :w /tmp/xxxx(如果是c文件就.c拉) 保存在/tmp下面 2 从tmp中复制到有权限的目录下面 cp /tmp xxxx ./(当前目录) 方式2::w !sudo tee ...
- 878. Nth Magical Number
A positive integer is magical if it is divisible by either A or B. Return the N-th magical number. ...
- Codeforces732F Tourist Reform
求出无向图的所有边双联通分量,然后缩点就成了一颗树. 然后我们选取最大的那个边双联通分量作为根,这样我们就可以确定所有割边的方向了. 对于边双联通分量里面的边,我们随便dfs一下就可以把它变成强连通分 ...
- Planning CodeForces - 854C
Planning CodeForces - 854C 题意:有n架航班,第i架原先的时候是在第i分钟起飞的.现在前k分钟无法有飞机起飞,因此需要调整安排表,延后飞机起飞.仍然要求每一分钟只有一架飞机起 ...
- Sublime Text 3列编辑
Sublime Text 3 的列编辑方式如下 1.使用鼠标 (Ubuntu 14.04验证通过) 不同的平台要使用不同的鼠标按钮: 1.1 OS X 鼠标左键 + Option 或: 鼠标中键 添加 ...
- selenium处理的操作
- [已读]JavaScript面向对象编程指南
又是一个忽悠人的书名,其实这本书的花了大量内容阐述JS的基础语法,BOM,DOM,事件,ajax(这个和很多js书一样).最后一章则是编程模式与设计模式. 我觉得与面向对象没多大关系,要算的话,pro ...
- Backbone学习记录(7)
事件委托 <form> <input type="text" class="txt"> <input type="but ...