【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;
}

 

 

posted @ 2017-06-06 15:47  GXZlegend  阅读(331)  评论(0编辑  收藏  举报