[BZOJ2555]-SubString-后缀自动机+LCT维护parent树

说在前面

今天写了好多后缀自动机的题啊…
感觉me又变强了hhhhh(然而这一定是错觉=w=


题目

BZOJ2555传送门

题目大意

给定一个初始字符串S,现在需要支持以下两种操作:
1. 在S后面添加一段字符串
2. 查询某个字符串在 S 中出现的次数(可重)
强制在线

输入输出格式

输入输出格式比较复杂,这里就不写了…
可以直接去B站上看,不是权限题


解法

如果没有第一种操作,这就是一道 后缀自动机 模板题了(参见「SPOJ NSUBSTR - Substrings」)
那么如何维护第一种操作呢= =?
注意到添加字符串只会在尾部加入,那这就是后缀自动机的增量构造,然而增量构造是会修改parent树的,因此信息需要动态维护。具体的,每加入一个字符(假设这个当前节点为node),需要维护的操作是:在parent树上连接一个节点,修改一个节点的父亲,更新链的信息。发现这不就是LCT干的事情嘛= =,然后直接无脑套LCT维护parent树就行了

LCT维护有两种方式,第一种是维护子树信息(参见「BZOJ4530 大融合」),另一种就是常规的打标记,me使用的是第一种方式

注意一个小地方,mask在decode函数里是传参,外面的mask是不会变的


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , Q , mask , qlen , id_c ;
char ss[600005] , qs[3000005] ;
struct SAM_Node{
    int len , id ;
    SAM_Node *ch[26] , *par ;
}w[1200005] , *tw = w , *SAM_root , *last ;
struct Splay_Node{
    bool isMain ;
    int isiz , rsiz , id ;
    Splay_Node *fa , *ch[2] ;
    void update(){
        rsiz = isMain ;
        if( ch[0] ) rsiz += ch[0]->rsiz + ch[0]->isiz ;
        if( ch[1] ) rsiz += ch[1]->rsiz + ch[1]->isiz ;
    }
}p[1200005] , *tp = p ;

void newNode( SAM_Node *&nd , int len , bool isMain ){
    nd = ++tw ;
    nd->id = ++id_c , nd->len = len ;

    Splay_Node *t = ++tp ;
    t->id = id_c ;
    t->rsiz = t->isMain = isMain ;
}

void decode(){
    int tmp = mask ;
    for( int j = 0 ; j < qlen ; j ++ ){
        tmp = ( tmp * 131 + j ) % qlen ;
        swap( qs[j] , qs[tmp] ) ;
    }
}

bool isRoot( Splay_Node *nd ){
    if( !nd->fa ) return true ;
    return nd->fa->ch[0] != nd && nd->fa->ch[1] != nd ;
}

void Rotate( Splay_Node *nd , short aim ){
    Splay_Node *x = nd->ch[aim] ;
    if( nd->fa ){
        if( nd->fa->ch[0] == nd ) nd->fa->ch[0] = x ;
        else if( nd->fa->ch[1] == nd ) nd->fa->ch[1] = x ;
    } x->fa = nd->fa ;
    if( x->ch[aim^1] ){
        x->ch[aim^1]->fa = nd ;
    } nd->ch[aim] = x->ch[aim^1] ;

    x->ch[aim^1] = nd , nd->fa = x ;
    nd->update() ;
    x->update() ;
}

// int topp ;
// Splay_Node *sta[1200005] ;
void Splay( Splay_Node *nd ){
/*  
    Splay_Node *tmp = nd ;
    for( ; !isRoot( tmp ) ; tmp = tmp->fa ) sta[++topp] = tmp ;
    sta[++topp] = tmp ;
    for( ; topp ; topp -- ) sta[topp]->pushdown() ;
*/
    while( !isRoot( nd ) ){
        Splay_Node *fa = nd->fa , *gdfa = fa->fa ;
        short pn = ( fa->ch[1] == nd ) , pf ;
        if( !isRoot( fa ) ){
            pf = ( gdfa->ch[1] == fa ) ;
            if( pn == pf ) Rotate( gdfa , pf ) , Rotate( fa , pn ) ;
            else           Rotate( fa , pn ) , Rotate( gdfa , pf ) ;
        } else Rotate( fa , pn ) ;
    }
}

void Access( Splay_Node *nd ){
    Splay_Node *las = NULL ;
    while( nd ){
        Splay( nd ) ;
        if( las ) nd->isiz -= las->rsiz + las->isiz ;
        if( nd->ch[1] ) nd->isiz += nd->ch[1]->rsiz + nd->ch[1]->isiz ;
        nd->ch[1] = las ;
        nd->update() ;
        las = nd ; nd = nd->fa ;
    }
}

void Link( Splay_Node *x , Splay_Node *y ){ // Node x's father is Node y 
    Access( y ) ; Splay( y ) ;
    Access( x ) ; Splay( x ) ;
    x->fa = y ;
    y->isiz += x->rsiz + x->isiz ;
}

void Cut( Splay_Node *x , Splay_Node *y ){  // similar to Link
    Access( y ) ; Splay( y ) ;
    Access( x ) ;
    y->ch[1] = x->fa = NULL ;
    y->update() ;
}

void Insert( const char &cc ){
//  printf( "%c start: \n" , cc ) ;
    SAM_Node *nd , *tmp = last ;
    newNode( nd , tmp->len + 1 , true ) ;
//  puts( "newNode ended\n" ) ;
    short id = cc - 'A' ;
    for( ; tmp && !tmp->ch[id] ; tmp = tmp->par )
        tmp->ch[id] = nd ;
    if( !tmp ) nd->par = SAM_root ;
    else {
    //  puts( "trans exist" ) ;
        SAM_Node *B = tmp->ch[id] ;
    //  printf( "B : %p\n" , tmp->ch[id] ) ; 
        if( tmp->len + 1 == B->len ) nd->par = B ;
        else{
        //  puts( "new nB" ) ;
            SAM_Node *nB ; newNode( nB , tmp->len + 1 , false ) ;
            memcpy( nB->ch , B->ch , sizeof( nB->ch ) ) ;

        //  puts( " Link " ) ;
            Link( ( p + nB->id ) , ( p + B->par->id ) ) ; nB->par = B->par ;
        //  puts( " Cut " ) ;
            Cut ( ( p + B->id )  , ( p + B->par->id ) )   ;
        //  puts( " Link " ) ;
            Link( ( p + B->id )  , ( p + nB->id ) ) ; nd->par = B->par = nB ;

            while( tmp && tmp->ch[id] == B ){
                tmp->ch[id] = nB ;
                tmp = tmp->par ;
            }
        }
    }
//  puts( "out Link" ) ;
    Link( ( p + nd->id ) , ( p + nd->par->id ) ) ;
    last = nd ;
//  printf( "%c successfully ended \n\n" , cc ) ;
}

int RUN(){
    SAM_Node *nd = SAM_root ;
    for( int i = 0 ; i < qlen ; i ++ ){
        short id = qs[i] - 'A' ;
        if( !nd->ch[id] ) return 0 ;
        nd = nd->ch[id] ;
    }
    Access( p + nd->id ) ; Splay( p + nd->id ) ; 
    return ( p + nd->id )->isiz + ( p + nd->id )->isMain ;
}
void solve(){
    char opt[10] ;
    for( int i = 1 ; i <= Q ; i ++ ){
        scanf( "%s%s" , opt , qs ) ;
        qlen = strlen( qs ) ; decode() ;
    //  printf( "%s %s\n" , opt , qs ) ;
        if( opt[0] == 'A' ){
            for( int j = 0 ; j < qlen ; j ++ ) Insert( qs[j] ) ;
        } else{
            int ans = RUN() ; mask ^= ans ;
            printf( "%d\n" , ans ) ;
        }
    }
}

int main(){
    newNode( SAM_root , 0 , false ) ; last = SAM_root ;
    scanf( "%d" , &Q ) ;
    scanf( "%s" , ss + 1 ) ; N = strlen( ss + 1 ) ;
    for( int i = 1 ; i <= N ; i ++ ) Insert( ss[i] ) ;
    solve() ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值