题意
给出字符串S和操作次数Q
ADD:往S后继续加一个字符串
QUERY:求给出的字符串在S中出现次数
考虑SAM,插入是O(n)的,查询位置时O(n)的,问题是怎么动态维护出sz[],sz[]其实就是在后缀树上u节点子树中结束节点的个数和。
考虑到要动态加边,删边,求子树和,可以用LCT维护。
每个np(新加节点)将其到根节点的路径上都加上1就行了。查询直接splay(i)查询sum[i]即可
不需要makeroot和split操作,因为根节点一定是1,而且可以直接cut子树
#include <bits/stdc++.h>
#define lc ch[x][0]
#define rc ch[x][1]
#define jh(x, y) (x ^= y, y ^= x, x ^= y)
#define ll long long
using namespace std;
inline void read(int &x) {
x = 0; int f = 1; char ch = getchar();
while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - 48; ch = getchar(); }
x *= f;
}
const int N = 6e6 + 10;
int mask, Q, ans;
char s[N];
void Gets(int mask) {
scanf("%s", s);
for (int j = 0, len = strlen(s); j < len; j++) {
mask = (mask * 131 + j) % len;
char t = s[j];
s[j] = s[mask];
s[mask] = t;
}
}
struct LCT {
int ch[N][2], fa[N], sum[N], tag[N];
inline int chk(int x) { return x == ch[fa[x]][1]; }
inline int nroot(int x) { return x == ch[fa[x]][chk(x)]; }
//pushdown的时候加上tag即可,其他和lct板子一样
inline void pushdown(int x) {
if (tag[x]) {
if (lc) sum[lc] += tag[x], tag[lc] += tag[x];
if (rc) sum[rc] += tag[x], tag[rc] += tag[x];
tag[x] = 0;
}
}
inline void Rotate(int x) {
int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k ^ 1];
if (nroot(y)) ch[z][chk(y)] = x;
ch[y][k] = w; ch[x][k ^ 1] = y;
if (w) fa[w] = y;
fa[x] = z; fa[y] = x;
}
inline void pushall(int x) { if (nroot(x)) pushall(fa[x]); pushdown(x); }
inline void splay(int x) {
pushall(x);
while (nroot(x)) {
int y = fa[x];
if (nroot(y)) Rotate((chk(x) ^ chk(y)) ? x : y);
Rotate(x);
}
}
inline void access(int x) {
for (int y = 0; x; x = fa[y = x])
splay(x), ch[x][1] = y;
}
//link操作很简单,因为插入的x节点一定是他的树的根节点,可以直接连
inline void link(int x, int y) { fa[x] = y; }
//cut可以直接把x的子树全部分离
inline void cut(int x) {
access(x), splay(x);
fa[ch[x][0]] = 0, ch[x][0] = 0;
}
//update先将要修改的点splay到根上后直接给新节点打上tag加1标记即可
inline void update(int x) {access(x); splay(x); tag[x] = sum[x] = 1;}
}lct;
struct SAM {
int ch[N][2], mxl[N], pre[N], last, cnt;
SAM() { last = cnt = 1; }
inline void Extend(int c) {
int p = last, np = ++cnt; last = np;
mxl[np] = mxl[p] + 1;
for (; p && !ch[p][c]; p = pre[p]) ch[p][c] = np;
//其实就是在修改pre时在lct,link起来
if (!p) { pre[np] = 1; lct.link(np, 1); lct.update(np); return; }
int q = ch[p][c];
if (mxl[q] == mxl[p] + 1) { pre[np] = q; lct.link(np, q); lct.update(np); return; }
int nq = ++cnt;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
mxl[nq] = mxl[p] + 1; pre[nq] = pre[q]; pre[q] = pre[np] = nq;
//在修改pre时,cut掉
lct.cut(q); lct.sum[nq] = lct.sum[q];
lct.link(q, nq); lct.link(np, nq); lct.link(nq, pre[nq]);
lct.update(np);
for (; ch[p][c] == q; p = pre[p]) ch[p][c] = nq;
}
inline void add() {
Gets(mask);
for (int i = 0, len = strlen(s); i < len; i++) Extend(s[i] - 'A');
}
inline int query() {
Gets(mask);
int p = 1;
for (int i = 0, len = strlen(s); i < len; i++) {
if (!ch[p][s[i] - 'A']) return 0;
p = ch[p][s[i] - 'A'];
}
lct.splay(p);
mask ^= lct.sum[p];
return lct.sum[p];
}
}sam;
char opt[5];
int main() {
read(Q);
scanf("%s", s + 1);
for (int i = 1, len = strlen(s + 1); i <= len; i++) sam.Extend(s[i] - 'A');
while (Q--) {
scanf("%s", opt);
if (opt[0] == 'A') sam.add();
else printf("%d\n", sam.query());
}
return 0;
}