AC自动机

基于trie和kmp
fail指针
学习过程:
oi wiki理解思路,听y总讲代码
模板一:acwing代码过不去,是要在求答案时跳fail的时候,打标记,每个点只走一次
模板二:连trie图
模板三:拓扑序求答案,拓扑排序不用单独写,build里顺手求一下就可以
阿狸打字机:理解fail树的思路,可以通过fail树子树概念理解包含子串的节点,顺便融合dfn序和树状数组(写一周才过)
S2025谐音替换:我靠这也太简单了!
模板:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
int n, tr[N][30], cnt[N], res[N], nxt[N], idx, c[N], r[N], vis[N];
string s;
void insert(int d){
	int p = 0;
	for(int i = 0; i < s.size(); i++){
		int t = s[i]-'a';
		if(!tr[p][t]) tr[p][t] = ++idx;
		p = tr[p][t];
	}
	cnt[p]++;
	c[d] = p;
}
void build(){
	queue<int> q;
	for(int i = 0; i < 26; i++) if(tr[0][i]) q.push(tr[0][i]);
	while(q.size()){
		int t = q.front();
		q.pop();
		for(int i = 0; i < 26; i++){
			int p = tr[t][i];
			if(!p) tr[t][i] = tr[nxt[t]][i];
			else{
				nxt[p] = tr[nxt[t]][i];
				q.push(p);
			}
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> s;
		insert(i);
	}
	build(); 
	cin >> s;
	for(int i = 0, j = 0; i < s.size(); i++){
		int t = s[i]-'a';
		j = tr[j][t];
		int p = j;
		res[p]++;
	}
	queue<int> q;
	for(int i = 1; i <= idx; i++) r[nxt[i]] ++;
	for(int i = 1; i <= idx; i++) if(!r[i]) q.push(i);
//拓扑排序,其实不用在外面单独写
	while(q.size()){
		int t = q.front();
		q.pop();
		r[nxt[t]]--;
		res[nxt[t]] += res[t];
		if(!r[nxt[t]]) q.push(nxt[t]);
	}
	for(int i = 1; i <= n; i++){
		cout << res[c[i]] << '\n';
	}
	return 0;
} 
posted @ 2026-07-20 21:15  宰杀方向盘  阅读(14)  评论(0)    收藏  举报