【bzoj2555】SubString 后缀自动机+LCT
题目描述
懒得写背景了,给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。
输入
第一行一个数Q表示操作个数
第二行一个字符串表示初始字符串init
接下来Q行,每行2个字符串Type,Str
Type是ADD的话表示在后面插入字符串。
Type是QUERY的话表示询问某字符串在当前字符串中出现了几次。
为了体现在线操作,你需要维护一个变量mask,初始值为0
读入串Str之后,使用这个过程将之解码成真正询问的串TrueStr。
询问的时候,对TrueStr询问后输出一行答案Result
然后mask = mask xor Result
插入的时候,将TrueStr插到当前字符串后面即可。
HINT:ADD和QUERY操作的字符串都需要解压
输出
询问的时候,对TrueStr询问后输出一行答案Result
样例输入
2
A
QUERY B
ADD BBABBBBAAB
样例输出
0
题解
后缀自动机+LCT
每次插入一个字符,对应的就相当于在parent树中np到root上所有节点的出现次数+1。
只要维护这样的修改,查询时找到位置直接输出即可。
然而parent树的结构是变化的,所以我们不得不使用Link-Cut-Tree来维护parent树的结构,同时来实现区间修改以及单点查询。
具体地,在插入节点后,需要access(x)splay(x)后打上加的标记。
查询时在后缀自动机中找到位置后将标记下传,然后输出答案。
细节有点多。。。需要注意的几点:原始字符串不需要解密、解密过程中不修改实际维护的mask值(解密函数的mask是数值传递)、查询时先把标记下传。
#include <cstdio> #include <cstring> #include <algorithm> #define N 1200010 using namespace std; int next[N][26] , pre[N] , dis[N] , last = 1 , tot = 1; int fa[N] , c[2][N] , w[N] , tag[N]; char opt[10] , str[N]; void read(int mask) { scanf("%s" , str); int i , l = strlen(str); for(i = 0 ; i < l ; i ++ ) mask = (mask * 131 + i) % l , swap(str[i] , str[mask]); } bool isroot(int x) { return c[0][fa[x]] != x && c[1][fa[x]] != x; } void add(int x , int a) { w[x] += a , tag[x] += a; } void pushdown(int x) { if(tag[x]) { w[c[0][x]] += tag[x] , tag[c[0][x]] += tag[x]; w[c[1][x]] += tag[x] , tag[c[1][x]] += tag[x]; tag[x] = 0; } } void update(int x) { if(!isroot(x)) update(fa[x]); pushdown(x); } void rotate(int x) { int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1; if(!isroot(y)) c[c[1][z] == y][z] = x; fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y; } void splay(int x) { update(x); while(!isroot(x)) { int y = fa[x] , z = fa[y]; if(!isroot(y)) { if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x); else rotate(y); } rotate(x); } } void access(int x) { int t = 0; while(x) splay(x) , c[1][x] = t , t = x , x = fa[x]; } void cut(int x) { access(x) , splay(x) , c[0][x] = fa[c[0][x]] = 0; } void link(int x , int y) { cut(x) , fa[x] = y; } void ins(int c) { int p = last , np = last = ++tot; dis[np] = dis[p] + 1; while(p && !next[p][c]) next[p][c] = np , p = pre[p]; if(!p) pre[np] = 1 , link(np , 1); else { int q = next[p][c]; if(dis[q] == dis[p] + 1) pre[np] = q , link(np , q); else { int nq = ++tot; memcpy(next[nq] , next[q] , sizeof(next[q])) , dis[nq] = dis[p] + 1; pre[nq] = pre[q] , link(nq , pre[q]); pre[q] = nq , link(q , nq); pre[np] = nq , link(np , nq); while(p && next[p][c] == q) next[p][c] = nq , p = pre[p]; update(q) , w[nq] = w[q]; } } access(np) , splay(np) , add(np , 1); } int query() { int t , i , l = strlen(str); for(i = 0 , t = 1 ; i < l ; i ++ ) { if(!next[t][str[i] - 'A']) return 0; t = next[t][str[i] - 'A']; } update(t); return w[t]; } int main() { int n , mask = 0 , i , l , t; scanf("%d%s" , &n , str) , l = strlen(str); for(i = 0 ; i < l ; i ++ ) ins(str[i] - 'A'); while(n -- ) { scanf("%s" , opt) , read(mask); if(opt[0] == 'A') { l = strlen(str); for(i = 0 ; i < l ; i ++ ) ins(str[i] - 'A'); } else printf("%d\n" , t = query()) , mask ^= t; } return 0; }