AC自动机

https://www.luogu.com.cn/problem/P5357

首先建 \(trie\) 树,将模式串逐个插入,记录每个模式串在 \(trie\) 树中对应的最后一个位置。

\(0\) 节点的孩子全部入队,做 \(bfs\)

当前节点为 \(u\),遍历每一条边 \(c\)\(26\) 个字母)。如果对应孩子存在,为孩子设置 \(fail\) 指针,指向 \(fail_u\) 对应的该字母边;如果不存在,修改 \(u\) 对应的该字母边,指向 \(fail_u\) 对应的该字母边。

遍历标准串,在 \(trie\) 树上跳转,给每次到达的点计数 \(+1\)。最后对 \(fail\) 指针构成的图做拓扑排序,父节点累加上子节点的计数。每个模式串对应最后一个位置的计数就是该模式串在标准串中出现的次数。

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;


void solve(){
	int n;
	cin >> n;

	vector<string> t(n+1);
	for (int i=1;i<=n;i++){
		cin >> t[i];
	}	

	string s;
	cin >> s;

	vector<int> tail(n+1);
	vector<vector<int>> next;
	next.push_back(vector<int>(26));
	
	for (int i=1;i<=n;i++){
		int u = 0;
		for (auto& ch:t[i]){
			int c = ch-'a';
			if (next[u][c]==0){
				next.push_back(vector<int>(26));
				next[u][c] = next.size()-1;
			}
			u = next[u][c];
		}

		tail[i] = u;
	}

	int tot = next.size();
	vector<int> fail(tot);
	queue<int> q;

	for (int c=0;c<26;c++){
		if (next[0][c]!=0){
			q.push(next[0][c]);
		}
	}	

	while (!q.empty()){
		int u = q.front();
		q.pop();

		for (int c=0;c<26;c++){
			if (next[u][c]!=0){
				fail[next[u][c]] = next[fail[u]][c];
				q.push(next[u][c]);
			}
			else{
				next[u][c] = next[fail[u]][c];
			}
		}
	}

	vector<int> cnt(tot);
	int u = 0;
	for (auto& ch:s){
		u = next[u][ch-'a'];
		cnt[u]++;
	}

	vector<int> ing(tot);
	for (int i=0;i<tot;i++){
		ing[fail[i]]++;	
	}

	for (int i=0;i<tot;i++){
		if (ing[i]==0){
			q.push(i);
		}
	}

	while (!q.empty()){
		int u = q.front();
		q.pop();

		cnt[fail[u]] += cnt[u];
		if (--ing[fail[u]]==0){
			q.push(fail[u]);
		}
	}

	for (int i=1;i<=n;i++){
		cout << cnt[tail[i]] << '\n';
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-26 17:11  kzssCCC  阅读(5)  评论(0)    收藏  举报