题解:P2414 [NOI2011] 阿狸的打字机

首先最朴素的想法可以分组,每次跑一个 dfs 优化的 ACAM。

换个角度想,我们算得慢的原因是如果不去每次跳 fail 就要跑满一个 \(O(|S|)\) 的 dfs,那如果我能快速的计算我有几个点可以跳 fail 跳到某一个点,那么就可以快速地计算它在我文本串中出现了几次。

我们回想 fail 的结构,他是一个树形的结构,如果我跳 fail 能到一个点,那么意味着我在 fail 树上在他的子树内,这很容易让我们想到 dfn 序维护子树信息。

我们可以对于一个字符串,把他在 trie 树上对应点打标记,树状数组计算子树内标记的个数,当然这样并不能使得分更多。

我们考虑每次打标记,我都要重新跑一遍 trie 树,有很多重复,如果直接在 trie 树上 dfs,到一个点就打上标记,回溯的时候清掉,遇到结束标签再去把他的答案统计掉就可以了。

code:

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

namespace mlyy {
	
	const int N = 2e5 + 100;
	
	int t[N];
	int lowbit(int x) {
		return x & (-x);
	}
	void add(int x, int d) {
		for (int i = x;i < N;i += lowbit(i)) t[i] += d;
	}
	int query(int x) {
		int res = 0;
		for (int i = x;i;i -= lowbit(i)) {
			res += t[i];
		}
		return res;
	}
	
	int idx;
	int son[N][26], Son[N][26];
	int fa[N];
	int fail[N], end[N];
	int tag[N];
	vector<int> edge[N];
	vector<pair<int, int>> q[N];
	void get_fail() {
		queue<int> q;
		for (int i = 0;i < 26;i++) {
			if (Son[0][i]) q.push(Son[0][i]);
		}
		while (q.size()) {
			int u = q.front(); q.pop();
			for (int i = 0;i < 26;i++) {
				int &cur = Son[u][i];
				if (cur) {
					fail[cur] = Son[fail[u]][i];
					q.push(cur);
				}
				else cur = Son[fail[u]][i]; 
			}
		}
		for (int i = 1;i <= idx;i++) {
			edge[fail[i]].push_back(i);
		}
	}
	int cnt = 0;
	int dfn[N], lst[N];
	void dfs1(int u) {
		dfn[u] = ++cnt;
		for (int v : edge[u]) {
			dfs1(v);
		}
		lst[u] = cnt;
	}
	
	int ans[N];
	void dfs2(int u) {
		add(dfn[u], 1);
		if (end[u]) {
			int y = end[u];
			for (auto i : q[y]) {
				int x = i.first, id = i.second;
				ans[id] = query(lst[tag[x]]) - query(dfn[tag[x]] - 1);
			}
		}
		for (int i = 0;i < 26;i++) {
			if (son[u][i]) dfs2(son[u][i]);
		}
		add(dfn[u], -1);
	}
	
	void main() {
		string s;
		cin >> s;
		int n = s.size();
		int cnt = 0;
		int pos = 0;
		for (int i = 0;i < n;i++) {
			if ('a' <= s[i] && s[i] <= 'z') {
				if (!son[pos][s[i] - 'a']) {
					son[pos][s[i] - 'a'] = ++idx;
					fa[idx] = pos;
				}
				pos = son[pos][s[i] - 'a'];
			}
			if (s[i] == 'B') pos = fa[pos];
			if (s[i] == 'P') {
				end[pos] = ++cnt;
				tag[cnt] = pos;
			}
		}
		
		for (int i = 0;i <= idx;i++) {
			for (int j = 0;j < 26;j++) {
				Son[i][j] = son[i][j];
			}
		}
		get_fail();
		int m;
		cin >> m;
		for (int i = 1;i <= m;i++) {
			int x, y;
			cin >> x >> y;
			q[y].push_back({x, i});
		}
		dfs1(0);
		dfs2(0);
		for (int i = 1;i <= m;i++) cout << ans[i] << "\n"; 
	}
}

signed main() {
	ios :: sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0); 
	mlyy::main();
	return 0;
}
posted @ 2026-03-07 11:47  yanbinmu  阅读(0)  评论(0)    收藏  举报