ac自动机有什么用呢?
一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过
学ac自动要先会trie树
我们先构建一个trie树,将ahs,shex,bcd,sha装入字典树
我们要建立以fail数组,fail指针指向的模式串部分前缀相同,如abce
和bcd
,我们找到c
发现下一个要找的不是e
,就跳到bcd
中的c
处
这里用bfs实现,首先将深度为1的结点全部装入队列中
我们对队列中的每个节点处理
比如处理a节点,
a连接s,如果匹配s好了,但是匹配不下去了,呢么就跳到a的fail节点连接的s处,fail(as)->trie(s),然后将s节点压入队列
构建fail数组的同时,也会需要改变trie树的连接
比如我们查询ac中出现'a','c'单词的个数,呢么我们先查a,cnt++,就然后查c,字典树中没有ac,我们要直接从根结点查询出发查询c
链接:https://www.luogu.org/problem/P3808
代码:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 1000005
using namespace std;
struct AC
{
int trie[MAXN][26];//字典树
int cntword[MAXN];//记录单词次数
int fail[MAXN];//回溯指针
int cnt;
void ins(char c[])//将单词装入字典树中
{
int now=0;
int len=strlen(c);
for(int i=0;i<len;i++)
{
int id=c[i]-'a';
if(trie[now][id]==0)
trie[now][id]=++cnt;
now=trie[now][id];
}
cntword[now]++;
}
void getfail()
{
fail[0]=0;
queue<int> que;
for(int i=0;i<26;i++)
{
if(trie[0][i]){
fail[trie[0][i]]=0;
que.push(trie[0][i]);
}
}
while(que.size())
{
int now=que.front();
que.pop();
for(int i=0;i<26;i++)
{
if(trie[now][i]){
fail[trie[now][i]]=trie[fail[now]][i];
que.push(trie[now][i]);
}
else{
trie[now][i]=trie[fail[now]][i];
}
}
}
}
int query(char s[])
{
int now=0,ans=0;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
now=trie[now][c];
for(int j=now;j&&cntword[j]!=-1;j=fail[j])
{
ans+=cntword[j];
cntword[j]=-1;
}
}
return ans;
}
}AA;
char ss[MAXN];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",ss+1);
AA.ins(ss+1);
}
AA.getfail();
scanf("%s",ss+1);
printf("%d\n",AA.query(ss+1));
return 0;
}
链接2:https://www.luogu.org/problem/P3796
查询文章中出现次数最大的单词,并按顺序输出
代码:
简单标记一下
#include<bits/stdc++.h>
#define ll long long
#define MAXN 30005
using namespace std;
struct AC
{
int trie[MAXN][26];//字典树
int cntword[MAXN];//记录单词次数
int fail[MAXN];//回溯指针
int vis[MAXN],pos[MAXN];
int cnt;
vector<int> vc;
void init()
{
vc.clear();
cnt=0;
memset(vis,0,sizeof(vis));
memset(pos,0,sizeof(pos));
memset(cntword,0,sizeof(cntword));
memset(fail,0,sizeof(fail));
memset(trie,0,sizeof(trie));
}
void ins(char c[],int v)//将单词装入字典树中
{
int now=0;
int len=strlen(c);
for(int i=0;i<len;i++)
{
int id=c[i]-'a';
if(trie[now][id]==0)
trie[now][id]=++cnt;
now=trie[now][id];
}
vc.push_back(now);
cntword[now]++;
pos[now]=v;
}
void getfail()
{
fail[0]=0;
queue<int> que;
for(int i=0;i<26;i++)
{
if(trie[0][i]){
fail[trie[0][i]]=0;
que.push(trie[0][i]);
}
}
while(que.size())
{
int now=que.front();
que.pop();
for(int i=0;i<26;i++)
{
if(trie[now][i]){
fail[trie[now][i]]=trie[fail[now]][i];
que.push(trie[now][i]);
}
else{
trie[now][i]=trie[fail[now]][i];
}
}
}
}
void query(char s[])
{
int now=0;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
now=trie[now][c];
for(int j=now;j&&cntword[j]!=-1;j=fail[j])
{
if(cntword[j]!=0)
vis[j]++;
}
}
}
}AA;
char cc[200][100];
char ss[1000005];
int main()
{
int n;
while(scanf("%d",&n)&&n)
{
AA.init();
for(int i=1;i<=n;i++)
{
scanf("%s",cc[i]+1);
AA.ins(cc[i]+1,i);
}
AA.getfail();
scanf("%s",ss+1);
AA.query(ss+1);
int ans=0,res;
for(int i=0;i<AA.vc.size();i++)
{
int v=AA.vc[i];
ans=max(ans,AA.vis[v]);
}
printf("%d\n",ans);
for(int i=0;i<AA.vc.size();i++)
{
int v=AA.vc[i];
if(AA.vis[v]==ans){
printf("%s\n",cc[AA.pos[v]]+1);
}
}
}
return 0;
}
https://vjudge.net/problem/POJ-2778#author=lypl
题意:
有m种DNA序列是致病的,问长为n且不包含致病序列的DNA有多少种
解析:
ac自动机+矩阵快速幂
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define mod 100000
#define ll long long
#define MAXN 10005
#define N 101
using namespace std;
int idx[200];
int trie[MAXN][5];
int fail[MAXN];
int vis[MAXN];
int cnt,sz;
void init()
{
memset(trie,0,sizeof(trie));
memset(fail,0,sizeof(fail));
memset(vis,0,sizeof(vis));
cnt=0;
}
void ins(char c[])
{
int now=0;
int len=strlen(c);
for(int i=0;i<len;i++)
{
int id=idx[c[i]];
if(trie[now][id]==0)
trie[now][id]=++cnt;
now=trie[now][id];
}
vis[now]=1;
}
void getfail()
{
fail[0]=0;
queue<int> que;
for(int i=1;i<=4;i++){
if(trie[0][i])
que.push(trie[0][i]);
}
while(que.size())
{
int now=que.front();
que.pop();
for(int i=1;i<=4;i++)
{
if(trie[now][i]){
fail[trie[now][i]]=trie[fail[now]][i];
que.push(trie[now][i]);
}
else{
trie[now][i]=trie[fail[now]][i];
}
vis[trie[now][i]]|=vis[trie[fail[now]][i]];
}
}
}
struct matrix
{
ll a[N+1][N+1];
matrix(){
memset(a,0,sizeof(a));
}
friend matrix operator*(matrix A,matrix B)
{
matrix C;
for(int i=0;i<sz;i++){
for(int j=0;j<sz;j++){
for(int k=0;k<sz;k++){
C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
}
}
}
return C;
}
};
matrix qpow(matrix A,ll m)//矩阵A的m次幂
{
matrix ans;//单位矩阵
for(int i=0;i<sz;i++)
ans.a[i][i]=1;
while(m)
{
if(m&1) ans=ans*A;
A=A*A;
m>>=1;
}
return ans;
}
int main()
{
idx['A']=1;idx['T']=2;idx['C']=3;idx['G']=4;
int n,m;
char ss[11];
while(scanf("%d%d",&m,&n)!=EOF)
{
init();
for(int i=1;i<=m;i++)
scanf("%s",ss),ins(ss);
sz=cnt+1;
getfail();
matrix A;
for(int i=0;i<=cnt;i++)
{
if(vis[i])
continue;
for(int j=1;j<=4;j++){
if(vis[trie[i][j]])
continue;
A.a[i][trie[i][j]]++;
}
}
A=qpow(A,n);//A矩阵n次
ll ans=0;
for(int i=0;i<=cnt;i++)
{
ans+=A.a[0][i];
ans=ans%mod;
}
printf("%lld\n",ans);
}
return 0;
}