SubString SAM+LCT

4 篇文章 0 订阅
2 篇文章 0 订阅

P5212 SubString

题意

给出字符串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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值