洛谷 P5357 【模板】AC自動機(二次加強版)

題目鏈接

https://www.luogu.org/problem/P5357

分析

相對於一般的AC自動機(或者常寫的Trie圖),

本題卡時主要在查詢時暴力向上跳 failfail 指針;

優化方法是按拓撲序更新答案,複雜度爲 O(T)O(|T|)

具體來說,按求 failfail 時BFS序的倒序依次更新每個節點對應答案。

注意模式串會有重複。

AC代碼

#include <cstdio>
#include <cstring>
#include <stack>
#include <queue>

using namespace std;

const int maxn = 2e5 + 5, maxs = 2e6 + 5;

int n, to[maxn], ans[maxn];
char str[maxs];
stack<int> st;

struct AC_Automaton {
	int ch[maxn][26], end[maxn], cnt[maxn], fail[maxn], tot;

	AC_Automaton() {
		memset(ch[0], 0, sizeof(ch[0]));
		memset(end, 0, sizeof(end));
		memset(cnt, 0, sizeof(cnt));
		memset(fail, 0, sizeof(fail));
		tot = 0;
	}

	int add() {
		++tot;
		memset(ch[tot], 0, sizeof(ch[tot]));
		return tot;
	}

	int insert(char* s, int id) {
		int p = 0;
		for (int i = 1; s[i]; ++i) {
			if (!ch[p][s[i] - 'a']) ch[p][s[i] - 'a'] = add();
			p = ch[p][s[i] - 'a'];
		}
		if (!end[p]) end[p] = id;
		return end[p];
	}

	void build() {
		queue<int> q;
		for (int i = 0; i < 26; ++i)
			if (ch[0][i]) q.push(ch[0][i]);
		while (!q.empty()) {
			int u = q.front();
			q.pop(), st.push(u);
			for (int i = 0; i < 26; ++i) {
				if (ch[u][i])
					fail[ch[u][i]] = ch[fail[u]][i], q.push(ch[u][i]);
				else ch[u][i] = ch[fail[u]][i];
			}
		}
	}

	void query(char* s) {
		int p = 0;
		for (int i = 1; s[i]; ++i)
			p = ch[p][s[i] - 'a'], ++cnt[p];
		while (!st.empty()) {
			int u = st.top();
			st.pop();
			ans[end[u]] = cnt[u];
			cnt[fail[u]] += cnt[u];
		}
	}
} ac;

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%s", str + 1);
		to[i] = ac.insert(str, i);
	}
	ac.build();
	scanf("%s", str + 1);
	ac.query(str);
	for (int i = 1; i <= n; ++i) printf("%d\n", ans[to[i]]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章