[BZOJ4556]-[Tjoi2016&Heoi2016]字符串-后缀自动姬+线段树合并+倍增

说在前面

me发现me学高级算法都要学傻了
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊.jpg


题目

BZOJ4556传送门
看题可进传送门


解法

懒得写解法了
反正自己没有想出来
想到要reverse字符串,然后要建出后缀自动姬,没了
其实思路和BZOJ2746是差不多的,都利用到了LCA的性质,感觉还是比较经典了
clover_hxy的题解 传送门


下面是自带大长度的代码

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

char ss[100005] ;
int N , M , SAMid , tp , head[200005] ;

//------------------------------------------------

struct SAM_Node{
    int len , id , isMain;
    SAM_Node *ch[26] , *par ;
} *SAM_root , *las , w[200005] , *tw = w , *arc[100005] ;

struct Seg_Node{
    int siz ;
    Seg_Node *ch[2] ;
    void update(){
        siz = 0 ;
        if( ch[0] ) siz += ch[0]->siz ;
        if( ch[1] ) siz += ch[1]->siz ;
    }
} *root[200005] ;

struct Path{
    int pre , to ;
}p[200005] ;

//------------------------------------------------

void new_SAM_Node( SAM_Node *&nd , int len ){
    nd = ++tw ;
    nd->id = ++SAMid ; nd->len = len ;
}

void new_Seg_Node( Seg_Node *&nd ){
    nd = new Seg_Node() ; nd->siz = 0 ;
    nd->ch[0] = nd->ch[1] = NULL ;
}

void In( int t1 , int t2 ){
    p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
}

//------------------------------------------------

void Insert( int id , int ith ){
    SAM_Node *nd , *tmp = las ;
    new_SAM_Node( nd , tmp->len + 1 ) ;
    nd->isMain = ith , arc[ith] = nd ;

    for( ; tmp && !tmp->ch[id] ; tmp = tmp->par ) 
        tmp->ch[id] = nd ;
    if( !tmp ) nd->par = SAM_root ;
    else{
        SAM_Node *B = tmp->ch[id] ;
        if( B->len == tmp->len + 1 ) nd->par = B ;
        else{
            SAM_Node *nB ; new_SAM_Node( nB , tmp->len + 1 ) ;
            nB->par = B->par ;
            B->par = nd->par = nB ;
            memcpy( nB->ch , B->ch , sizeof( nB->ch ) ) ;
            while( tmp && tmp->ch[id] == B )
                tmp->ch[id] = nB , tmp = tmp->par ;
        }
    } las = nd ;
}

void Insert( Seg_Node *&nd , int lf , int rg , int pos ){
    new_Seg_Node( nd ) , nd->siz = 1 ;
    if( lf == rg ) return ;
    int mid = ( lf + rg ) >> 1 ;
    if( pos <= mid ) Insert( nd->ch[0] , lf , mid , pos ) ;
    else             Insert( nd->ch[1] , mid+1,rg , pos ) ;
}

void Merge( Seg_Node *&x , Seg_Node *y ){
    if( !y ) return ;
    if( !x ){ x = y ; return ; }
    Seg_Node *nd ; new_Seg_Node( nd ) ;
    *nd = *x ; x = nd ;
    Merge( nd->ch[0] , y->ch[0] ) ;
    Merge( nd->ch[1] , y->ch[1] ) ;
    nd->update() ;
}

//------------------------------------------------

int fa[18][200005] , mdep , logg ;
void dfs( int u , int dep ){
    SAM_Node *nd = &w[u] ;
    mdep = max( mdep , dep ) ;
    if( nd->isMain ) Insert( root[u] , 1 , SAMid , nd->isMain ) ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        fa[0][v] = u , dfs( v , dep + 1 ) ;
        Merge( root[u] , root[v] ) ;
    }
}

void preWork(){
    for( int i = 2 ; i <= SAMid ; i ++ )
        In( w[i].par->id , w[i].id ) ;
    dfs( 1 , 1 ) ; fa[0][1] = 1 ;
    for( logg = 1 ; 1 << ( logg + 1 ) <= mdep ; logg ++ ) ;
    for( int i = 1 ; i <= logg ; i ++ )
        for( int j = 1 ; j <= SAMid ; j ++ )
            fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
}

//------------------------------------------------

int a , b , c , d ;
bool Query( Seg_Node *nd , int lf , int rg , int L , int R ){
    if( !nd ) return false ;
    if( L <= lf && rg <= R ) return ( bool )nd->siz ;
    int mid = ( lf + rg ) >> 1 ;
    if( R <= mid ) return Query( nd->ch[0] , lf , mid , L , R ) ;
    if( L >  mid ) return Query( nd->ch[1] , mid+1,rg , L , R ) ;
    return Query( nd->ch[0] , lf , mid , L , R ) || Query( nd->ch[1] , mid+1,rg , L , R ) ;
}

bool check( int mid ){
    int u = arc[c]->id ;
    for( int i = logg ; i >= 0 ; i -- )
        if( w[ fa[i][u] ].len >= mid ) u = fa[i][u] ;
    return Query( root[u] , 1 , SAMid , b + mid - 1 , a ) ;
}



void solve(){
    for( int i = 1 ; i <= M ; i ++ ){
        scanf( "%d%d%d%d" , &a , &b , &c , &d ) ;
        a = N-a+1 , b = N-b+1 , c = N-c+1 , d = N-d+1 ;//rev :: d||||||c  b||||||a 
        int lf = 0 , rg = min( c - d + 1 , a - b + 1 ) , ans ;
        while( lf <= rg ){
            int mid = ( lf + rg ) >> 1 ;
            if( check( mid ) )
                ans = mid , lf = mid + 1 ;
            else rg = mid - 1 ;
        } printf( "%d\n" , ans ) ;
    }
}

//------------------------------------------------

int main(){
    new_SAM_Node( SAM_root , 0 ) ; las = SAM_root ;
    scanf( "%d%d%s" , &N , &M , ss + 1 ) ;
    for( int i = N ; i ; i -- ) // 反串
        Insert( ss[i] - 'a' , N - i + 1 ) ;
    preWork() ; solve() ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值