指针-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用来在一篇文章中匹配一个模式串:但 ...
随机推荐
- U3D Navigation
让我们来一起粗步认识一下NavMesh的简单使用 首先我们建立一个新场景,在新场景我们创建 一个地形或者创建一个Plane, 然后在其上面用Cube或者其它的建立一些障碍物 再创建自己需要为其设置自动 ...
- Centos6.6安装后一些常见问题详解
<一>.centos6.6通过VM最小化安装后上不了网的解决方法: 在安装centos6.6时,没有在网络设置中设置网卡自动启动的,安装完系统后,是不能联网的,解决方法如下: vi/etc ...
- Vs2013+opencv2.4.12+x64用VideoCapture无法打开视频
环境变量中匹配的是x86的opencv_ffmpeg244.dll,与项目不匹配,需在项目exe文件同目录下添加X:\opencv\opencv2.4.12\build\x64\vc12\bin\op ...
- HDU 5882 Balanced Game (水题)
题意:问 nnn 个手势的石头剪刀布游戏是否能保证出每种手势胜率都一样. 析:当每种手势的攻防个数完全相等才能保证平衡,所以容易得出 nnn 是奇数时游戏平衡,否则不平衡. 也就是说打败 i 的和 i ...
- 张高兴的 .NET Core IoT 入门指南:(三)使用 I2C 进行通信
什么是 I2C 总线 I2C 总线(Inter-Integrated Circuit Bus)是设备与设备间通信方式的一种.它是一种串行通信总线,由飞利浦公司在1980年代为了让主板.嵌入式系统或手机 ...
- bzoj 4589: Hard Nim【线性筛+FWT+快速幂】
T了两次之后我突然意识到转成fwt形式之后,直接快速幂每次乘一下最后再逆回来即可,并不需要没此次都正反转化一次-- 就是根据nim的性质,先手必输是所有堆个数异或和为0,也就变成了一个裸的板子 #in ...
- 笔记-JavaWeb学习之旅
junit单元测试 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值 白盒测试:需要些代码,关注程序具体的执行流程 Junit使用: 白盒测试 步骤: 定义一个测试类(测试用例) 定义 ...
- Codeforces Round #513解题报告(A~E)By cellur925
我是比赛地址 A:Phone Numbers $Description$:给你一串数字,问你能组成多少开头为8的11位电话号码. $Sol$:统计8的数量,与$n$%11作比较. #include&l ...
- 进程动态拦截注入API HOOK
最近工作中遇到一个问题,需要通过程序界面进行判断程序的运行状态,刚开始认为很简单,不就是一个窗体控件获取,获取Button的状态和Text.刚好去年干过该事情,就没太在意,就把优先级排到后面了,随着项 ...
- 51Nod 1021 石子归并(动态规划)
#include <iostream> #include <algorithm> #include <string> #include <iostream&g ...