指针-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用来在一篇文章中匹配一个模式串:但 ...
随机推荐
- AutoIt脚本在做自动化操作的时候,如何进行错误捕获?
我的自动化脚本在运行的时候,会生成一个界面,点击该页面上的按钮能够进行自动化操作. 经常遇到的一个问题是: 脚本运行一半,GUI程序出现了异常情况,这个时候,再次点击生成的界面上的按钮,不会有任何反应 ...
- BackTrack5(BT5)各版本下载
BT5R3(最新版本)http://www.nigesb.com/backtrack-5-r3-released.html BT5R2KDE版32位: http://ftp.halifax.rwth ...
- Programming With Objective-C---- Encapsulating Data ---- Objective-C 学习(三) 封装数据
Programming with Objective-C Encapsulating Data In addition to the messaging behavior covered in t ...
- E20170507-ts
prompt n. 提示; 提示符 object n. 物体; 目标; 宾语; asterisk * n. 星号,星状物; Ampersand & Slash n. 斜线; ...
- 洛谷 - P2335 - 位图 - 简单dp
https://www.luogu.org/problemnew/show/P2335 假如我们使用dp的话,每次求出一个点的左上方.右上方.左下方.右下方的最近的白点的距离.那么只是n²的复杂度.这 ...
- poj1308【并查集】
= =.如果输入的两个数相等.就不是一颗树啊,不能自己指向自己. 水.(瞎开的数组). //#include <bits/stdc++.h> #include<iostream> ...
- hdu1085 Holding Bin-Laden Captive!【生成函数】
列出生成函数的多项式之后暴力乘即可 #include<iostream> #include<cstdio> #include<cstring> using name ...
- 51Nod 1092 回文字符串
最开始毫无头绪,然后参照了一位dalao的博客,思路是一个正序的字符串将其逆序,然后求最长公共子序列(LCS),emm也属于动态规划. #include <iostream> #inclu ...
- Travelling HDU - 3001
Travelling HDU - 3001 方法:3进制状态压缩dp(更好的方法是预处理出每个状态数字对应的y数组,然后用刷表,时间复杂度可以少一个n) #include<cstdio> ...
- 关于bootstrap table的server分页
首先是bootstrap初始化的表格参数: // 初始化Table oTableInit.Init = function() { $('#booksTable').bootstrapTable({ u ...