[BZOJ2555]-SubString-后缀自动机+LCT维护parent树
2018-01-16 22:15
337 查看
说在前面
今天写了好多后缀自动机的题啊…感觉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() ; }
相关文章推荐
- BZOJ 2555 Substring(后缀自动机+LCT子树维护)
- BZOJ 2555: SubString 后缀自动机 LCT
- [后缀自动机 LCT] BZOJ 2555 SubString
- [BZOJ2555]SubString(后缀自动机+lct)
- bzoj 2555: SubString 后缀自动机+lct
- BZOJ 2555: SubString 后缀自动机+LCT
- bzoj 2555: SubString 后缀自动机+LCT
- BZOJ 2555 SubString 后缀自动机+LCT
- [后缀自动机][LCT] BZOJ 2555: SubString
- 【BZOJ2555】SubString 后缀自动机+LCT
- [BZOJ2555]SubString(后缀自动机+LCT)
- BZOJ 2555: SubString [后缀自动机 LCT]
- 字符串(LCT,后缀自动机):BZOJ 2555 SubString
- 【bzoj2555】SubString 后缀自动机+LCT
- [BZOJ]2555 Substring 后缀自动机&LCT
- bzoj2555 SubString(后缀自动机+LCT)
- BZOJ2555 SubString 后缀自动机+LCT
- [BZOJ2555][LCT][后缀自动机]SubString
- 后缀自动机 + LCT 【bzoj2555】SubString
- 【bzoj2555】SubString LCT+后缀自动机