AC自动机

AC自动机是用于解决线性复杂度下 \(n\) 个字符串 \(s_{1},s_{2},\dots ,s_{n}\)在字符串\(t\)中的查询问题。
参考题目:
洛谷P3808【模板】AC 自动机(简单版)
洛谷P3796【模板】AC 自动机(加强版)
洛谷P5357【模板】AC 自动机(二次加强版)
算法思路:

  1. 首先对\(s_{1}\dots s_{n}\)建一棵trie树。
  2. 我们会发现两个字符串可能有重叠部分,可以极大地减少对比次数。
    当一个结点的子结点失配时,它可以找到一个使它最大减少对比次数的结点(失配结点)继续匹配。
    如何寻找?
    假设此结点的父结点已经找到失配结点,从父结点的失配结点的子结点中寻找,若没有,再向父结点的失配结点的失配结点的子结点中寻找,以此类推,直到根结点。
    但这样最劣$ \Theta (n^{2})$ ,考虑优化,每个结点都只有一个失配结点,所以将结点和失配结点建一条边得到一棵树。依此结点作为父结点时,一定在到根的这条链上。然后我们可以递推记录使得每次 $ \Theta (1)$操作。此纪录同样可以帮助之后的查询操作。
  3. 查询如果子结点有,就跳子结点,记录有,就跳记录,都没有就跳根节点。
    在每个结点,走一遍失配链,更新答案。
    这样可过加强版。
    打标记就可过简单版。
  4. 但这依然过不了二次加强版,因为它在走失配链时还是$ \Theta (n^{2})$。这里可以发现树上的边都是指向父亲结点的有向边。这样就不用走失配链了,直接记录就可以了。
    全都记录完后,树形DP就可以了。
简单版
#include<iostream>
#include<cstring>
using namespace std;
int n;
char s[1000010];
char t[1000010];
int l;
struct node{
	int sp;
	int zhi;
	bool p;
	int son[30];
}dian[1000010];
int tot=0;
void add(int i,int j){
	if(j>l){
		dian[i].zhi++;
		return ;
	}
	if(dian[i].son[s[j]-'a'+1]==0){
		dian[i].son[s[j]-'a'+1]=++tot;
	}
	add(dian[i].son[s[j]-'a'+1],j+1);
	return ;
}
int dui[1000010],head=1,tail=0;
void bfs(){
	dui[++tail]=0;
	while(head<=tail){
		int i=dui[head];
		head++;
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]!=0){
				dui[++tail]=dian[i].son[j];
			} 
		}
		if(i==0){
			for(int j=1;j<=26;j++){
				if(dian[i].son[j]!=0){
					dian[dian[i].son[j]].sp=0;
				} 
			}
			continue;
		}
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]!=0){
				dian[dian[i].son[j]].sp=dian[dian[i].sp].son[j];
			} 
		}
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]==0){
				dian[i].son[j]=dian[dian[i].sp].son[j];
			} 
		}
	}
}
int ans=0;
void work(){
	int j=0;
	for(int i=1;i<=l;i++){
		int k=j;
		while(k!=0&&dian[k].p==0){
			dian[k].p=1;
			ans+=dian[k].zhi;
			dian[k].zhi=0;
			k=dian[k].sp;
		}
		j=dian[j].son[t[i]-'a'+1];
	}
	int k=j;
	while(k!=0&&dian[k].p==0){
		dian[k].p=1;
		ans+=dian[k].zhi;
		dian[k].zhi=0;
		k=dian[k].sp;
	}
} 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		l=strlen(s+1);
		add(0,1);
	}
	bfs();
	scanf("%s",t+1);
	l=strlen(t+1);
	work();
	cout<<ans;
	return 0;
}
加强版
#include<iostream>
#include<cstring>
using namespace std;
int n;
char s[200][100];
char t[1000010];
int l;
struct node{
	int zhi;
	int sp;
	int son[30];
	int chuan[200];
	int cnt;
}dian[20000];
int tot=0;
void add(int i,int j,int k){
	if(j>l){
		dian[i].zhi=1;
		dian[i].chuan[++dian[i].cnt]=k;
		return ;
	}
	if(dian[i].son[s[k][j]-'a'+1]==0){
		dian[i].son[s[k][j]-'a'+1]=++tot;
	}
	add(dian[i].son[s[k][j]-'a'+1],j+1,k);
	return ;
}
int dui[20000];
int head=1,tail=0;
void bfs(){
	head=1,tail=0;
	dui[++tail]=0;
	while(head<=tail){
		int i=dui[head];
		head++;
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]!=0){
				dui[++tail]=dian[i].son[j];
			}
		}
		if(i==0){
			for(int j=1;j<=26;j++){
				if(dian[i].son[j]!=0){
					dian[dian[i].son[j]].sp=0;
				}
			}
			continue;
		}
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]!=0){
				dian[dian[i].son[j]].sp=dian[dian[i].sp].son[j];
			}
			else{
				dian[i].son[j]=dian[dian[i].sp].son[j];
			}
		}
	}
}
int tong[200];
void work(){
	int j=0;
	for(int i=1;i<=l;i++){
		if(dian[j].son[t[i]-'a'+1]!=0){
			j=dian[j].son[t[i]-'a'+1];
		}
		else{
			j=0;
		}
		int k=j;
		while(k!=0){
			if(dian[k].cnt>0){
				for(int l=1;l<=dian[k].cnt;l++){
					tong[dian[k].chuan[dian[k].cnt]]++;
				}
			}
			k=dian[k].sp;
		}
	}
}
int main(){
	cin>>n;
	while(n!=0){
		for(int i=0;i<=tot;i++){
			dian[i].zhi=0;
			dian[i].sp=0;
			dian[i].cnt=0;
			for(int j=1;j<=26;j++){
				dian[i].son[j]=0;
			}
		}
		tot=0;
		for(int i=1;i<=n;i++){
			tong[i]=0;
			scanf("%s",s[i]+1);
			l=strlen(s[i]+1);
			add(0,1,i);
		}
		bfs();
		scanf("%s",t+1);
		l=strlen(t+1);
		work();
		int ans=0;
		for(int i=1;i<=n;i++){
			ans=max(ans,tong[i]);
		}
		cout<<ans<<endl;
		for(int i=1;i<=n;i++){
			if(ans==tong[i]){
				l=strlen(s[i]+1);
				for(int j=1;j<=l;j++){
					cout<<s[i][j];
				}
				cout<<endl; 
			}
		}
		cin>>n;
	}
	return 0;
}
二次加强版
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int n;
char s[200010];
char t[2000010];
int l;
struct node{
	int zhi;
	int sp;
	int son[30];
	vector<int> chuan;
	int cnt;
}dian[200010];
struct nod{
	int to;
	int nxt;
}edge[200010];
int hed[200010],tal;
void addedge(int u,int v){
	edge[++tal].to=v;
	edge[tal].nxt=hed[u];
	hed[u]=tal;
}
int tot;
void add(int i,int j,int k){
	if(j>l){
		dian[i].cnt++;
		dian[i].chuan.push_back(k);
		return ;
	}
	if(dian[i].son[s[j]-'a'+1]==0){
		dian[i].son[s[j]-'a'+1]=++tot;
		dian[tot].chuan.push_back(0);
	}
	add(dian[i].son[s[j]-'a'+1],j+1,k);
	return ;
} 
int dui[200010],head=1,tail=0;
void bfs(){
	dui[++tail]=0;
	while(head<=tail){
		int i=dui[head];
		head++;
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]!=0){
				dui[++tail]=dian[i].son[j];
			}
		}
		if(i==0){
			for(int j=1;j<=26;j++){
				if(dian[i].son[j]!=0){
					dian[dian[i].son[j]].sp=0;
					addedge(0,dian[i].son[j]);
				}
			}
			continue;
		}
		for(int j=1;j<=26;j++){
			if(dian[i].son[j]>0){
				dian[dian[i].son[j]].sp=dian[dian[i].sp].son[j];
				addedge(dian[dian[i].sp].son[j],dian[i].son[j]);
			}
			else{
				dian[i].son[j]=dian[dian[i].sp].son[j]; 
			}
		}
	}
}
int tong[200010];
void work(){
	int j=0;
	for(int i=1;i<=l;i++){
		j=dian[j].son[t[i]-'a'+1];
		dian[j].zhi++;
	}
}
int dfs(int u){
	int sum=dian[u].zhi;
	for(int i=hed[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		sum+=dfs(v);
	}
	for(int i=1;i<=dian[u].cnt;i++){
		tong[dian[u].chuan[i]]=sum;
	}
	return sum;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		l=strlen(s+1);
		add(0,1,i);
	}
	bfs();
	scanf("%s",t+1);
	l=strlen(t+1);
	work();
	dfs(0);
	for(int i=1;i<=n;i++){
		cout<<tong[i]<<endl;
	}
	return 0;
}
posted @ 2023-01-30 14:16  zzzzzz2  阅读(30)  评论(0编辑  收藏  举报