大家都不喜欢指针,但是这个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自动机的更多相关文章

  1. 初学AC自动机

    前言 一直听说\(AC\)自动机是一个很难很难的算法,而且它不在\(NOIP\)提高组范围内(这才是关键),所以我一直没去学. 最近被一些字符串题坑得太惨,于是下定决心去学\(AC\)自动机. 简介 ...

  2. AC自动机 - 关于Fail指针

    fail指针可以说是AC自动机里最难理解的东西,怎样更好的理解AC自动机的fail指针? 先来看一幅图: 看这幅图上的fail指针是怎么构造的. 树上的词分别是: { he , hers , his ...

  3. AC自动机总结及板子(不带指针)

    蒟蒻最近想学个AC自动机简直被网上的板子搞疯了,随便点开一个都是带指针的,然而平时用到指针的时候并不多,看到这些代码也完全是看不懂的状态.只好在大概理解后自己脑补(yy)了一下AC自动机的代码,居然还 ...

  4. hdu 1277 AC自动机入门(指针版和数组版)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1277 推荐一篇博客(看思路就可以,实现用的是java): https://www.cnblogs.co ...

  5. bzoj 2434 AC自动机 + fail指针建树 + 树状数组

    思路:我们先跟着它给定的字符串走把字典树建出来,求出fail指针,我们考虑两个字符串 A和B, 如果想要求B中有多少A的子串,转换一下就是有多少个B的前缀的后缀包含A,这个在AC自动机 的状态图中很容 ...

  6. CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)

    What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...

  7. ac自动机fail树上按询问建立上跳指针——cf963D

    解法看着吓人,其实就是为了优化ac自动机上暴力跳fail指针.. 另外这题对于复杂度的分析很有学习价值 /* 给定一个母串s,再给定n个询问(k,m) 对于每个询问,求出长度最小的t,使t是s的子串, ...

  8. POJ 1625 Censored!(AC自动机->指针版+DP+大数)题解

    题目:给你n个字母,p个模式串,要你写一个长度为m的串,要求这个串不能包含模式串,问你这样的串最多能写几个 思路:dp+AC自动机应该能看出来,万万没想到这题还要加大数...orz 状态转移方程dp[ ...

  9. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

随机推荐

  1. 微信小程序服务类目大坑:特殊行业服务类目所需资质材料

    作为一个技术开发人员,遇到特殊行业服务类目所需资质材料,只能叫苦连天了,妈的,这个不是技术可以解决的问题,如果技术可以解决的问题都不是问题. 百牛信息技术bainiu.ltd整理发布于博客园 特殊行业 ...

  2. JAVA Synchronized (二)

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  3. 【转】java对象——new对象的理解

    学了好长时间的java对于java中的对象一直没有理清楚,今天楼主对java中的对象进行了整理,希望对大家有帮助. 理解和使用java中的对象,我们首先了解一下构造方法与对象的创建.  类是面向对象语 ...

  4. js dom element 属性整理(原创)

    最近去几家公司面试,发现大多数时候面试的内容考的都是原生的js语法和属性,所以我决心整理一下原生的dom元素的属性. 首先,我我们需要获取一个element元素 <li id="2&q ...

  5. 在Entity Framework 中实现继承关系映射到数据库表

    继承关系映射到数据库表中有多种方式: 第一种:TPH(table-per-hiaerachy) 每一层次一张表 (只有一张表) 仅使用名为父类的类型名的一张表,它包含了各个子类的所有属性信息,使用区分 ...

  6. windows动态磁盘导致的分区问题

    上次说到由于装双系统导致我的win7启动不了了,一直以为是不是在ubuntu的安装界面点错了什么东西导致的,甚至认为是不是server的安装程序有点bug,直到今天继续折腾才发现了问题所在,跟ubun ...

  7. hibernate的基础学习

    工具类: public class H3Util { private static final SessionFactory sessionFactory = buildSessionFactory( ...

  8. 看鸟哥的Linux私房菜的一些命令自我总结(三)

    -修改文件创建时间或创建新文件 touch [-acdmt] -a  :仅修改访问时间 -c  :仅修改文件的时间,若该文件不存在则不创建新文件 -d  :后面可以接想要修改的日期而不用当前的日期 - ...

  9. Swift里计数相关的小细节

    Swift里对于字符串这些引入了index型,相对其他语言而言字符操作更安全了,但是问题就是一不注意搞错范围就会有各种离奇的bug. 在讲主题前,先说个小细节. Swift里非常严密的定义了一大堆字符 ...

  10. (水题)Codeforces - 650A - Watchmen

    http://codeforces.com/contest/650/problem/A 一开始想了很久都没有考虑到重复点的影响,解欧拉距离和曼哈顿距离相等可以得到 $x_i=x_j$ 或 $y_i=y ...