ac自动机
AC自动机
AC 自动机是 以 Trie 的结构为基础,结合 KMP 的思想 建立的自动机,用于解决多模式匹配等任务。
关键就是fail指针的建立
首先就是常规的建立trie树,再根据bfs构造整个数组的fail指针,同时更新exist数组
然后就是查询操作
详见注释版代码
例题
这里放出洛谷的三题ac自动机,代码还是得照着自己的理解敲一遍,不然照着写,错在哪里都不知道
P3808 【模板】AC 自动机(简单版)
P3796 【模板】AC 自动机(加强版)
P5357 【模板】AC 自动机(二次加强版)
相关资料
1.b站视频-邋遢大哥233
可用于理解ac自动机板子,但其所给板子空间复杂度很高?
带注释
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
ac自动机模板题(简单版)
这个带注释
*/
const int maxm=1e6+5,inf=0x3f3f3f3f,mod=998244353;
ll n;
string ss[maxm],t;
set<ll> cnt;
struct acnode{
vector<int> exist;
acnode *fail=nullptr;
acnode *child[26]={nullptr};
};
void ac_insert(acnode *root,string s,int id){//建trie树,插入string
acnode *t=root;
for(int i=0;i<s.size();++i){
int x=s[i]-'a';
if(t->child[x]==nullptr)
t->child[x]=new acnode;
t=t->child[x];
}
t->exist.push_back(id);
return ;
}
void ac_build(acnode *root){//建立整个ac自动机的基础
acnode *t=root;
for(int i=0;i<n;++i){//构建初始的字典树
ac_insert(root,ss[i],i);
}
queue<acnode*> q;
q.push(root);
while(!q.empty()){//利用bfs更新所有fail指针
acnode *x=q.front();
q.pop();
for(int i=0;i<26;++i){
if(x->child[i]!=nullptr){
acnode *y=x->child[i],*fafail=x->fail;
while(fafail!=nullptr&&fafail->child[i]==nullptr)
fafail=fafail->fail;
if(fafail==nullptr)//此时fail=root
y->fail=root;
else//此时存在这么个child[i]的情况
y->fail=fafail->child[i];
if(y->fail->exist.size()){//更新exist数组
for(int j=0;j<y->fail->exist.size();++j){
y->exist.push_back(y->fail->exist[j]);
}
}
q.push(y);//记得加上y,实现bfs
}
}
}
return ;
}
void ac_query(acnode *root){
acnode *temp=root;
for(int i=0;i<t.size();++i){//扫描t串
int c=t[i]-'a';
while(temp->child[c]==nullptr&&temp->fail!=nullptr)
temp=temp->fail;
if(temp->child[c]!=nullptr)//存在这个字符
temp=temp->child[c];
else//不存在这个字符,此时tmp=root
continue;
if(temp->exist.size()){
for(int j=0;j<(temp->exist.size());++j){//此题此处用于统计出现情况
cnt.insert(temp->exist[j]);
}
}
}
return ;
}
void solve(){
acnode *root=new acnode;
cin>>n;
for(int i=0;i<n;++i){
cin>>ss[i];
}
ac_build(root);//建立ac自动机
cin>>t;
ac_query(root);//匹配
cout<<cnt.size()<<'\n';
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
无注释
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
ac自动机模板题
*/
const int maxm=1e6+5,inf=0x3f3f3f3f,mod=998244353;
ll n;
string ss[maxm],t;
set<ll> cnt;
struct acnode{
vector<int> exist;
acnode *fail=nullptr;
acnode *child[26]={nullptr};
};
void ac_insert(acnode *root,string s,int id){
acnode *t=root;
for(int i=0;i<s.size();++i){
int x=s[i]-'a';
if(t->child[x]==nullptr)
t->child[x]=new acnode;
t=t->child[x];
}
t->exist.push_back(id);
return ;
}
void ac_build(acnode *root){
acnode *t=root;
for(int i=0;i<n;++i){
ac_insert(root,ss[i],i);
}
queue<acnode*> q;
q.push(root);
while(!q.empty()){
acnode *x=q.front();
q.pop();
for(int i=0;i<26;++i){
if(x->child[i]!=nullptr){
acnode *y=x->child[i],*fafail=x->fail;
while(fafail!=nullptr&&fafail->child[i]==nullptr)
fafail=fafail->fail;
if(fafail==nullptr)
y->fail=root;
else y->fail=fafail->child[i];
if(y->fail->exist.size()){
for(int j=0;j<y->fail->exist.size();++j){
y->exist.push_back(y->fail->exist[j]);
}
}
q.push(y);
}
}
}
return ;
}
void ac_query(acnode *root){
acnode *temp=root;
for(int i=0;i<t.size();++i){
int c=t[i]-'a';
while(temp->child[c]==nullptr&&temp->fail!=nullptr)
temp=temp->fail;
if(temp->child[c]!=nullptr)
temp=temp->child[c];
else continue;
if(temp->exist.size()){
for(int j=0;j<(temp->exist.size());++j){
cnt.insert(temp->exist[j]);
}
}
}
return ;
}
void solve(){
acnode *root=new acnode;
cin>>n;
for(int i=0;i<n;++i){
cin>>ss[i];
}
ac_build(root);
cin>>t;
ac_query(root);
cout<<cnt.size()<<'\n';
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17520640.html