AC自动机模板

https://www.cnblogs.com/hyfhaha/p/10802604.html

易错点

多组数据初始化时,cnt要初始化为1,用完之后要对trie的son数组清0,其他可以不清0但是son必须清0,这个会影响拓扑和fail! 

如果RE,一定是trie开小了,这样cnt++才会访问越界,而不是s,T之类的开小了

#include<cstdio>//https://www.cnblogs.com/hyfhaha/p/10802604.html
#include<iostream>
#include<queue>
#include<cstring>
const int maxn=2e6+10;
using namespace std;
char s[maxn],T[maxn];//s为模板串,T为主串
int n,cnt,vis[200051],in[maxn],Map[maxn];//n为模板串个数,cnt为树下标,vis存放答案(每个模板串出现的次数),in为入度,Map记录模板串编号 
struct node {
	int son[26],fail,flag,ans;//flag记录这是第几个模板串(模板串编号) 
} trie[maxn];//字典树存储模板串 
queue<int> q;
void insert(char* s,int num) {//插入单词 num记录这是第几个模板串
	int u=1,len=strlen(s);
	for(int i=0; i<len; ++i) {
		int v=s[i]-'a';
		if(!trie[u].son[v]) trie[u].son[v]=++cnt;
		u=trie[u].son[v];
	}
	if(!trie[u].flag) trie[u].flag=num;
	Map[num]=trie[u].flag;
}
void getFail() {
	for(int i=0; i<26; i++) trie[0].son[i]=1;//0是虚点,所有儿子都是根节点1 为了防止bfs时根节点儿子fail指向自己 
	q.push(1);
	while(!q.empty()) {//bfs
		int u=q.front();//父节点 其的fail一定是求过的 
		q.pop();
		int Fail=trie[u].fail;//父节点的fail指向的节点 
		for(int i=0; i<26; i++) {
			int v=trie[u].son[i];//子节点 下面求他的fail 
			if(!v) {//如果子节点不存在 
				trie[u].son[i]=trie[Fail].son[i];
				continue;
			}
			trie[v].fail=trie[Fail].son[i];
			in[trie[v].fail]++;
			q.push(v);
		}
	}
}
void topu() {//拓扑更新fail的ans 
	for(int i=1; i<=cnt; ++i)
		if(in[i]==0) q.push(i);			//将入度为0的点全部压入队列里 如果入度为0,说明没有它不是别的节点的fail,那么就不需要被更新了 
	while(!q.empty()) {
		int u=q.front();				//队首的ans是已经更新过的,用队首更新子节点 
		q.pop();
		vis[trie[u].flag]=trie[u].ans;	//如果有flag标记就更新vis数组 flag是模板串编号 
		int v=trie[u].fail;				//v是u的fail连接的边 u->v 
		in[v]--;						//将唯一连出去的出边fail的入度减去(拓扑排序的操作)
		trie[v].ans+=trie[u].ans;		//更新fail的ans值
		if(in[v]==0) q.push(v);			//拓扑排序常规操作
	}
}
void query(char* s) {
	int u=1,len=strlen(s);
	for(int i=0; i<len; ++i)
		u=trie[u].son[s[i]-'a'],trie[u].ans++;
}
//以下为模板移植易错点:多组数据初始化时,cnt要初始化为1,用完之后要对trie的son数组清0,其他可以不清0但是son必须清0,这个会影响拓扑和fail! 
/*void init() {
	for(int i=0;i<=cnt;i++){
		trie[i].ans=trie[i].fail=trie[i].flag=0;
		for(int j=0;j<26;j++){
			trie[i].son[j]=0;
		}
	}
	memset(vis,0,sizeof vis);
	memset(in,0,sizeof in);
	memset(Map,0,sizeof Map);
	memset(flag,0,sizeof flag);
}*/
int main() {
	scanf("%d",&n);
	cnt=1;//易错点!如果不写这句话程序是错的 
	for(int i=1; i<=n; ++i) {
		scanf("%s",s);
		insert(s,i);
	}
	getFail();
	scanf("%s",T);
	query(T);
	topu();
	for(int i=1; i<=n; i++) printf("%d\n",vis[Map[i]]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值