AC自动机模板(拓扑优化)

题目

AC自动机模板(拓扑优化)

时间复杂度: \(O( \sum \left | T_{i} \right | + \left | S \right | )\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int tot=1,n,vis[N],Map[N],in[N];//编号从1开始;vis最终每个原始词编号的出现次数;Map[i]表示第i个原始词实际对应的那个flag
string T,S;
queue<int> q;
struct node{
	int son[26],flag,fail,cnt;//26个小写字母;flag为这个是哪个词的终点;cnt为访问计数
}tr[N];
void insert(string s,int num){//建Trie树
	int p=1;
	for(int i=0;i<s.size();i++){
		int u=s[i]-'a';
		if(!tr[p].son[u]) tr[p].son[u]=++tot;
		p=tr[p].son[u];
	}
	if(!tr[p].flag) tr[p].flag=num;//还没访问过就设为当前词编号
	Map[num]=tr[p].flag;//因为有重复的词,所以重复的也指向这里
}
void query(string s){//优化后,直接打标记就行(直接计数)
	int p=1;
	for(int i=0;i<s.size();i++){
		p=tr[p].son[s[i]-'a'];//这里注意要转换为整型
		tr[p].cnt++;//直接打上标记
	}
}
void getfail(){//设置失配指针(失配指针:当你匹配不了更长的前缀时,就退而求其次,匹配尽可能长的、已经出现过的一个真后缀作为新的起点)
	for(int i=0;i<26;i++){
		tr[0].son[i]=1;//初始化0的所有儿子都是1,这样任何从0号点的跳转,都会回到起点,很方便
	}
	q.push(1);//将根压入队列
	while(q.size()){
		int u=q.front();q.pop();
		int Fail=tr[u].fail;//就是fafail
		for(int i=0;i<26;i++){//遍历所有儿子
			int v=tr[u].son[i];//处理u的i儿子的fail,这样就可以不用记父亲了
			if(!v){//情况A:没有这个儿子
				tr[u].son[i]=tr[Fail].son[i];//直接路径压缩,直接向前跳(因为BFS按深度一层层处理,所以一定跳转目标已经被处理好)
				continue;
			}//情况B:有这个儿子
			tr[v].fail=tr[Fail].son[i];//设置失配指针(标准的AC自动机fail构建规则:当前字符串的最长后缀(v去掉第一个字符)等于已有前缀(Fail的i子节点代表的串))
			in[tr[v].fail]++;//记得加上入度
			q.push(v);//存在实节点才压入队列
		}
	}
}
void topu(){//向上汇总
	for(int i=1;i<=tot;i++){
		if(in[i]==0){
			q.push(i);//将入度为0的点全部压入队列里(即叶子)
		}
	}
	while(q.size()){//从叶子向根传递消息
		int u=q.front();q.pop();
		vis[tr[u].flag]=tr[u].cnt;//如果有flag终点标记,就把累计的cnt存进vis数组
		int v=tr[u].fail;in[v]--;//将唯一连出去的出边fail的入度减去(拓扑排序的操作)
		tr[v].cnt+=tr[u].cnt;//更新fail的cnt值,因为v是u的后缀,所以每访问到u,一定也访问到了v
		if(in[v]==0){
			q.push(v);//拓扑排序常规操作
		}
	}
}
int main(){
	cin.tie(0)->ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>T;
		insert(T,i);
	}
	getfail();
	cin>>S;query(S);
	topu();
	for(int i=1;i<=n;i++){
		cout<<vis[Map[i]]<<'\n';//原词可能重复,Map[i]找到了它实际对应的flag,然后输出那个flag的答案
	}
	return 0;
}
posted @ 2026-08-07 22:01  decentz  阅读(7)  评论(0)    收藏  举报