CF914F Substrings in a String

复盘 fsy 讲的题,写篇题解来报复社会

Description

维护一个支持单点修改的,能查询区间 \([l,\ r]\) 内指定文本串 \(t\) 出现次数的模式串 \(s\)

\(|s| \leq 10 ^ 5,\ \ q\leq 10 ^ 5,\ \ \sum |t| \leq 10 ^ 5\)

Solution

这玩意似乎并不好维护,询问很 SAM ,但似乎单点修改又需要重构,所以看起来只维护全局 SAM 并不能很好地解决这个问题。

再看数据,发现这很 CF 根号,我们可以考虑在重构上下点狠心,但是我们又不能直接重构,这样是一个 \(O(q \cdot n)\) 的恶心算法,考虑怎么把时间降下来。

发现有个 \(\sum\) 的条件,所以说我们应该用这玩意限制一下,比如说根号分治。

令阈值为 \(B\)

  1. \(|t| > B\) 时,我们直接重构整个 SAM ,根据条件,这种字符串数量不超过 \(\frac{\sum}{B}\) 个,总时间是 \(O(n \cdot \frac{\sum |t|}{B})\)

  2. \(|t| \leq B\) 时,我们考虑分块,对于询问区间遍及的每个块内进行重构,总时间是 \(O(\sum |t| \cdot \frac{n}{B})\)

这样就完了吗??没有!!

  • 因为 \(|t| \leq B\) ,所以有可能答案在两个且至多两个区间内产生,说还要对于每相邻两个块间的答案再次统计(长度一定要是左区间 \(|t| - 1\) + 右区间 \(|t| - 1\) ),总时间是 \(O(\sum |t| \cdot B)\)

总的来看将 \(B\) 定为 \(\sqrt{n}\) 就能得到一个正常的根号做法。

然而被 bitset 爆踩。。

Code

Code

/*

*/
#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, B = 322;
int n, m, q, k, blo[N], l[N], r[N], lzy[N];
char s[N], p[N];
inline int read() {
	char ch = getchar();
	int s = 0, w = 1;
	while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
	while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
	return s * w;
}
template 
struct SAM {
	int n, cnt, las, len[M], link[M], ch[M][26];
	int tong[M], rk[M], mx[M], siz[M];
	inline void init() {
		cnt = las = 1;
		memset(ch[1], 0, sizeof(ch[1]));
	}
	inline void SAM_stru(int c) {
		int cur = ++cnt, p = las;
		memset(ch[cur], 0, sizeof(ch[cur]));
		las = cur;
		len[cur] = len[p] + 1; siz[cur] = 1;
		while (p && !ch[p][c]) ch[p][c] = cur, p = link[p];
		if (!p) {link[cur] = 1; return ;}
		int q = ch[p][c];
		if (len[p] + 1 == len[q]) {link[cur] = q; return ;}
		int clo = ++cnt; siz[clo] = 0;
		link[clo] = link[q]; len[clo] = len[p] + 1;
		link[q] = link[cur] = clo;
		memcpy(ch[clo], ch[q], sizeof(ch[clo]));
		while (p && ch[p][c] == q) ch[p][c] = clo, p = link[p];
	}
	inline void Tong_sort() {
		memset(tong, 0, sizeof(int) * (cnt + 1));
		for (int i = 1; i <= cnt; ++i) ++tong[len[i]];
		for (int i = 1; i <= cnt; ++i) tong[i] += tong[i - 1];
		for (int i = 1; i <= cnt; ++i) rk[tong[len[i]]--] = i;
		for (int i = cnt; i > 1; --i) siz[link[rk[i]]] += siz[rk[i]];
	}
	inline void rebuild(int l, int r) {
		init();
		for (int i = l; i <= r; ++i) SAM_stru(s[i] - 'a');
		Tong_sort();
	}
	inline int calc(char *t, int m) {
		int p = 1;
		for (int i = 1; i <= m; ++i) {
			p = ch[p][t[i] - 'a'];
			if (!p) return 0;
		}
		return siz[p];
	}
};
SAM t;
SAM b[B << 1];
inline int solve() {
	int lt = read(), rt = read(), bl = blo[lt], br = blo[rt], res = 0;
	scanf("%s", p + 1); m = strlen(p + 1);
	if (m > B || bl == br) return t.rebuild(lt, rt), t.calc(p, m);
	t.rebuild(lt, r[bl]); res += t.calc(p, m); 
	t.rebuild(l[br], rt); res += t.calc(p, m);
	for (int i = bl + 1; i < br; ++i) {
		if (lzy[i]) lzy[i] = 0, b[i].rebuild(l[i], r[i]);
		res += b[i].calc(p, m);
	}
	for (int i = bl + 1; i <= br; ++i) {
		t.rebuild(max(lt, r[i - 1] - m + 2), min(rt, l[i] + m - 2));
		res += t.calc(p, m); 
	}
	return res;
}
int main() {
	scanf("%s", s + 1); n = strlen(s + 1);
	k = (n - 1) / B + 1;
	for (int i = 1; i <= k; ++i) {
		l[i] = r[i - 1] + 1; r[i] = r[i - 1] + B;
		for (int j = l[i]; j <= r[i]; ++j) blo[j] = i;
		lzy[i] = 1;
	}
	r[k] = n;
	q = read();
	for (int i = 1, op, x; i <= q; ++i) {
		op = read();
		if (op == 1) {
			x = read(); char c = getchar();
			s[x] = c; lzy[blo[x]] = 1;
		}
		else printf("%d\n", solve());
	}
	return 0;
}

posted @ 2022-03-12 20:39  Illusory_dimes  阅读(29)  评论(0编辑  收藏  举报