本文主要是介绍Keywords Search AC自动机QAQ,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
AC自动机,一直以来都以为是一个非常高大上的算法,其实它还真的挺高大上的。
首先来说,ac自动机的思想与kmp类似,需要自己模拟来理解。
给两个博客:
http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html
https://blog.csdn.net/KXL5180/article/details/88093307
还有bibi上有个挺好的视频可以看一下,有助于理解
板子分为3个部分来:
首先对于需要的数组
int cnt,root;//cnt表示树的某个节点位置;root表示根,其实就是0;
int fail[maxn];//表示上一个他这个字符的位置。
int ch[maxn][30];//用来表示一个新的节点,存的值是下一个字符的位置
int val[maxn];//表示某个节点的有效值,就是以该点串串结尾的个数
1.建树。建立字典树,这里开了静态的空间来装线段树。
int cnt,root; //N=26;
void init(){cnt=0;root=newnode();}int newnode()//建立新的一个节点,并初始化fail指针与val值为0;{for(int i=0;i<N;i++)ch[cnt][i]=0;val[cnt]=fail[cnt++]=0;return cnt-1;}
void insert(char *s)//插入某一个字符串{int len=strlen(s);int u=0;for(int i=0;i<len;i++){int v=s[i]-'a';if(!ch[u][v])//如果有该点就不开新节点ch[u][v]=newnode();//没有就开新节点u=ch[u][v];}val[u]++;//每当加完一个新的字符串,结尾其实就是节点要val值加1,表示这点是某个串串的结束}
2.建立fail指针。
fail指针的意义就是如果找一个点的时候,你可以找他的fail指针找到有相同作用的点,当某个点寻找下一点失败的时候有fail指针引导你下次应该跳转的位置。自己并不能说的很清楚。
可以参考博客:https://blog.csdn.net/u013371163/article/details/60469145
void getfail(){queue<int >q;int u=0;for(int i=0;i<N;i++)//找到连接root节点的点,加入队列fail指针已经是0了不用重新赋值。if(ch[u][i])q.push(ch[u][i]);while(!q.empty())//类似于bfs的搜索方式{u=q.front();q.pop();for(int i=0;i<N;i++){if(ch[u][i])//如果u节点有下对应的(i+‘a’){fail[ch[u][i]]=ch[fail[u]][i];//这个点fail指针就是连接到他父亲的fail指针对应位置下的那个'a'+i字母的位置。//因为假设u点的fail的位置为v,那么v这个点的作用其实同u点,那么ch[v][i]即v下边如果有那个'a'+i字母,那么这个位置之前已经知道了,赋过去就行//如果没有呢,那么其实之前开辟新节点也是处理过的,fail[cnt][i]=0,那么他的fail指针就是指向0,就是根节点。q.push(ch[u][i]);}elsech[u][i]=ch[fail[u]][i];//如果u没有'a'+i这个字母,就把它的位置(注意是位置)直接跳到上边解释的位置,查询的时候模拟一下就知道了}}}
3.查找某个串的匹配串有几种。
int query(char *s)//查询的时候是需要自己模拟一下{int len=strlen(s);int u=0,ans=0;for(int i=0;i<len;i++){int v=s[i]-'a';u=ch[u][v];//找到u下边的'a'+i字母的位置for(int j=u;j&&~val[j];j=fail[j])//fail指针走到根节点,或者某个点走过了{ans+=val[j];//加上某个节点的值,其实就是加上串尾点val[j]=-1;//标记为走过}}return ans;}
AC代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define inff 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define me(a,b) memset(a,b,sizeof(a))
#define min4(a,b,c,d) min(min(a,b),min(c,d))
#define min3(x,y,z) min(min(x,y),min(y,z))
#define max4(a,b,c,d) max(max(a,b),max(c,d))
#define max3(x,y,z) max(max(x,y),max(y,z))
typedef long long ll;
using namespace std;
const int maxn=5e5+5;
const int maxm=1e6+5;
const int N=26;
struct AC
{int cnt,root;int fail[maxn];int ch[maxn][30];int val[maxn];int newnode(){for(int i=0;i<N;i++)ch[cnt][i]=0;val[cnt]=fail[cnt++]=0;return cnt-1;}void init(){cnt=0;root=newnode();}void insert(char *s){int len=strlen(s);int u=0;for(int i=0;i<len;i++){int v=s[i]-'a';if(!ch[u][v])ch[u][v]=newnode();u=ch[u][v];}val[u]++;}void getfail(){queue<int >q;int u=0;for(int i=0;i<N;i++)//找到连接root节点的点,加入队列fail指针已经是0了不用重新赋值。if(ch[u][i])q.push(ch[u][i]);while(!q.empty())//类似于bfs的搜索方式{u=q.front();q.pop();for(int i=0;i<N;i++){if(ch[u][i])//如果u节点有下对应的(i+‘a’){fail[ch[u][i]]=ch[fail[u]][i];//这个点fail指针就是连接到他父亲的fail指针对应位置下的那个'a'+i字母的位置。//因为假设u点的fail的位置为v,那么v这个点的作用其实同u点,那么ch[v][i]即v下边如果有那个'a'+i字母,那么这个位置之前已经知道了,赋过去就行//如果没有呢,那么其实之前开辟新节点也是处理过的,fail[cnt][i]=0,那么他的fail指针就是指向0,就是根节点。q.push(ch[u][i]);}elsech[u][i]=ch[fail[u]][i];//如果u没有'a'+i这个字母,就把它的位置(注意是位置)直接跳到上边解释的位置,查询的时候模拟一下就知道了}}}int query(char *s)//查询的时候是需要自己模拟一下{int len=strlen(s);int u=0,ans=0;for(int i=0;i<len;i++){int v=s[i]-'a';u=ch[u][v];//找到u下边的'a'+i字母的位置for(int j=u;j&&~val[j];j=fail[j])//fail指针走到根节点,或者某个点走过了{ans+=val[j];//加上某个节点的值,其实就是加上串尾点val[j]=-1;//标记为走过}}return ans;}
}AC;
char str[maxm];
int main()
{int t,n;cin>>t;while(t--){scanf("%d",&n);AC.init();while(n--){scanf("%s",str);AC.insert(str);}AC.getfail();scanf("%s",str);printf("%d\n",AC.query(str));}return 0;
}
这篇关于Keywords Search AC自动机QAQ的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!