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;
}
posted on 2023-07-02 12:19  Qiansui  阅读(14)  评论(0编辑  收藏  举报