您的位置:首页 > 运维架构

[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() ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: