AC自动机

板子题.

AC自动机的建立(指针版本,数组版本附在注释后面)
fail树

const int M=26,N=1010000,mod=1e9+7;

struct node{
	node *son[M],*go[M],*fail;//go[i]表示当前节点位置匹配i时,最终会跳到哪个节点才能匹配成功,fail就类似于KMP的next数组 
}pool[N],*cur=pool,*root,*d[210];//cur表示当前指向pool的位置 

node *newnode(){//新创立一个节点 
	return cur++;
} 

node *q[N];

void build(){
	int t=0;
	q[t++]=root;//bfs序更新每个节点的fail以及go(这个信息的更新位置顺序就是按照深度从小到大,即bfs序),q[t++]=0;
	for(int i=0;i<t;i++){
		node *u=q[i];
		for(int j=0;j<M;j++){
			if(u->son[j]){//如果说存在这个儿子的话
				u->go[j]=u->son[j];//在u这个位置,如果要匹配j的话,正好u的下一个有j,那么肯定是go[u][j]=son[u][j]最优的
				
				if(u==root) u->son[j]->fail=root;//他儿子的fail肯定等于他本身,即根节点,fail[son[u][j]]=root;
				else u->son[j]->fail=u->fail->go[j];//否则他儿子的fail就等于他自己的fail然后再找下一个能匹配到的,fail[son[u][j]]=go[fail[u]][j]
				/*这一步就相当于求next数组,能肯定的是u要先退到next[u],然后在跟son[u][j]匹配 */
				q[t++]=u->son[j];//q[t++]=son[u][j]
			}else {
				if(u==root)u->go[j]=root;//go[u][j]=root;
				else u->go[j]=u->fail->go[j]; //go[u][j]=go[fail[u]][j];
			}
		} 
	}
} 

void solve() {
	int n;cin>>n;
	root=newnode();//字典树的根节点 
	for(int i=0;i<n;i++){
		string t;cin>>t;
		int m=t.size();
		node *p=root;//每次从根节点往下走,int p=0;
		for(int j=0;j<m;j++){
			int w=t[j]-'a';
			if(!p->son[w])p->son[w]=newnode();//如果说没有这个节点,就新建一个,son[p][w]=++idx;
			p=p->son[w];//p=son[p][w];
		}
		d[i]=p;//记录每个单词都走到了字典树上的哪个位置,d[i]=p;
	}
	build();//建立ACAM
}

AC代码

#include <bits/stdc++.h>

#define int long long
#define x first
#define y second
#define endl '\n'

using namespace std;
const int M=26,N=1010000,mod=1e9+7;

struct node{
	node *son[M],*go[M],*fail;
	int cnt;//表示这个节点经过了多少次 
}pool[N],*cur=pool,*root,*d[210];

node *newnode(){
	return cur++;
} 

node *q[N];
char tt[N];
string s;
int t=0;

void build(){
	q[t++]=root;
	for(int i=0;i<t;i++){
		node *u=q[i];
		for(int j=0;j<M;j++){
			if(u->son[j]){
				u->go[j]=u->son[j];
				
				if(u==root) u->son[j]->fail=root;
				else u->son[j]->fail=u->fail->go[j];
				q[t++]=u->son[j];
			}else {
				if(u==root)u->go[j]=root;
				else u->go[j]=u->fail->go[j];
			}
		} 
	}
} 

void solve() {
	int n;cin>>n;
	root=newnode();
	for(int i=0;i<n;i++){
		cin>>tt;
		int m=strlen(tt);
		node *p=root;
		for(int j=0;j<m;j++){
			int w=tt[j]-'a';
			s.push_back(tt[j]);
			if(!p->son[w])p->son[w]=newnode();
			p=p->son[w];
		}
		d[i]=p;
		s.push_back(0);//相当于是空格 
	}
	build();
	
	node *p=root;
	for(char ch:s){
		if(ch==0)p=root;
		else p=p->go[ch-'a'];
		p->cnt++;
	}  
	
	//树形dp,求每个串的出现次数
	for(int i=t-1;i>=1;i--){//默认深度从大到小的顺序更新 
		q[i]->fail->cnt+=q[i]->cnt;
	} 
	
	for(int i=0;i<n;i++){
		cout<<d[i]->cnt<<endl;
	}
}
signed main() {
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
//	cin>>T; 
	while (T--) solve();
}
posted @ 2024-11-06 13:47  MENDAXZ  阅读(1)  评论(0编辑  收藏  举报