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\) 。
-
当 \(|t| > B\) 时,我们直接重构整个 SAM ,根据条件,这种字符串数量不超过 \(\frac{\sum}{B}\) 个,总时间是 \(O(n \cdot \frac{\sum |t|}{B})\) 。
-
当 \(|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;
}