把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

AC自动机学习笔记

这里给三个板子做一个总结
简单版
AC自动机就是KMP+Trie,fail指针在多模式串之间跳跃来进行匹配。
fail指针有一种很妙的写法:

inline void bfs(){
	register int i,tot;
	for(i=0;i<26;i++)if(f[0].son[i]) q.push(f[0].son[i]);
	while(!q.empty()){
		now=q.front();q.pop();
		for(i=0;i<=25;i++){
			tot=f[now].son[i];
			if(f[now].son[i]) f[tot].fail=f[f[now].fail].son[i],q.push(tot);
			else f[now].son[i]=f[f[now].fail].son[i];
		}
	}
}

把一棵树补成一张图,拿来更好地匹配。
代码实现:

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,k,x,y,z,cnt,ans,now,nows;
char s[1000039],a[1000039];
struct tree{int son[27],cnt,fail;}f[1000039];
inline void get(){
	m=strlen(a+1);
	register int i;now=0;
	for(i=1;i<=m;i++){
		if(!f[now].son[a[i]-'a'])f[now].son[a[i]-'a']=++cnt;
		now=f[now].son[a[i]-'a'];
	}
	f[now].cnt++;
}
queue<int> q;
inline void bfs(){
	register int i;
	for(i=0;i<=25;i++){
		now=f[0].son[i];
		if(now) q.push(now);
	}
	while(!q.empty()){
		nows=q.front();
		q.pop();
		for(i=0;i<=25;i++){
			now=f[nows].son[i];
			if(now){
				f[now].fail=f[f[nows].fail].son[i];
				q.push(now);
			}
			else f[nows].son[i]=f[f[nows].fail].son[i];
		}
	}
}
inline int check(){
	register int i;
	n=strlen(s+1);
	now=0;
	for(i=1;i<=n;i++){
		nows=f[now].son[s[i]-'a'];
		if(nows&&~f[nows].cnt){
			ans+=f[nows].cnt;
			f[nows].cnt=-1;
			nows=f[nows].fail;
		}
		now=f[now].son[s[i]-'a'];
	}
	return ans;
}
int main(){
//	freopen("1.in","r",stdin);
	register int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++) cin>>a+1,get();
	bfs();
	cin>>s+1;
	printf("%d\n",check());
}

加强版
这个可以匹配到以后暴力跳fail指针,给沿线每个指针加上\(1\)
这样的复杂度是\(O(70|S|+\sum\limits{|T|})\)
然而这个\(70\)可以消掉,注意我们暴力跳了很多无用的路径,所以可以把这些放到最后弄。一遍拓扑就好了。时间复杂度\(O(|S|+\sum\limits{|T|})\)常数略大。
其实二次加强版是一样的,就不赘述了。
代码实现:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n,m,k,x,y,z,ans[200039],t,cnt,id[200039],in[200039],pus,now;
struct AC{int tot,cnt,son[26],fail;}f[200039];
char s[200039],a[2000039];
inline void get(int x){
	register int i,now=0;
	for(i=1;i<=m;i++)now=f[now].son[s[i]-'a']?f[now].son[s[i]-'a']:(f[now].son[s[i]-'a']=++cnt);
	id[x]=now;f[now].cnt++;
}
queue<int> q;
inline void bfs(){
	register int i,tot;
	for(i=0;i<26;i++)if(f[0].son[i]) q.push(f[0].son[i]);
	while(!q.empty()){
		now=q.front();q.pop();
		for(i=0;i<=25;i++){
			tot=f[now].son[i];
			if(f[now].son[i]) f[tot].fail=f[f[now].fail].son[i],q.push(tot);
			else f[now].son[i]=f[f[now].fail].son[i];
		}
	}
}
inline void find(){
	register int i,now=0;
	for(i=1;i<=m;i++)
	now=f[now].son[a[i]-'a'],f[now].tot++;
}
int main(){
	freopen("1.in","r",stdin);
	register int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%s",s+1);
		m=strlen(s+1);get(i);
	}bfs();
	scanf("%s",a+1);m=strlen(a+1);find();
	for(i=1;i<=cnt;i++)in[f[i].fail]++,ans[i]=f[i].tot;
	for(i=1;i<=cnt;i++)if(!in[i])  q.push(i);
	while(!q.empty()){
		now=q.front();q.pop();
		in[f[now].fail]--;ans[f[now].fail]+=ans[now];
		if(!in[f[now].fail]) q.push(f[now].fail);
	}
	for(i=1;i<=n;i++) printf("%d\n",ans[id[i]]);
}
posted @ 2021-02-03 13:54  275307894a  阅读(53)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end