BZOJ 4556 [HEOI2016/TJOI2016]字符串

BZOJ 4556 [HEOI2016/TJOI2016]字符串

其实题解更多是用后缀数组+数据结构的做法,貌似也不好写。

反正才学了 sam 貌似比较简单的做法。

还是得先二分,然后倍增跳到 $ s[c...c+mid-1] $ 所在的节点,然后看看有没有 endpos 在 $ a+mid-1...b $ 内就好了。

复杂度是二分和倍增的 $ nlog^2n $。

其实这道题因为只用求 endpos 是否存在啥的 vector + lower_bound 貌似都可以过了。。但其实启发式合并也不好写,还是写一下线段树合并吧,有些时候会要求维护一些其他的信息,而且复杂度只在最初合并有区别,并不是复杂度瓶颈。

开始线段树写成 On T了半天海星。。

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "ctime"
using namespace std;
#define MAXN 200006
int n , m , ps;
char ch[MAXN];
namespace solve {

    int T[MAXN*20] , ls[MAXN*20] , rs[MAXN*20] , rt[MAXN] , cn;
    void add( int& rt , int l , int r , int p ) {
        if( !rt ) rt = ++ cn;
        ++ T[rt];
        if( l == r ) return;
        int m = l + r >> 1;
        if( p <= m ) add( ls[rt] , l , m , p );
        else add( rs[rt] , m + 1 , r , p );
    }
    int merge( int u , int v , int l , int r ) {
        if( !u || !v ) return u ^ v;
        int cur = ++ cn , m = l + r >> 1;
        T[cur] = T[u] + T[v];
        if( l == r ) return cur;
        ls[cur] = merge( ls[u] , ls[v] , l , m );
        rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
        return cur;
    }
    int que( int rt , int l , int r , int L , int R ) {
        if( !rt ) return 0;
        if( L <= l && R >= r ) return T[rt];
        int m = l + r >> 1 , res = 0;
        if( L <= m ) res += que( ls[rt] , l , m , L , R );
        if( R > m ) res += que( rs[rt] , m + 1 , r , L , R );
        return res;
    }

    int son[MAXN][26] , len[MAXN] , par[MAXN] , lst[MAXN];
    int last , cnt;

    int head[MAXN] , to[MAXN] , nex[MAXN] , ecn;
    void ade( int u , int v ) {
        to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn;
    }
    void addall( ) {
        for( int i = 2 ; i <= cnt ; ++ i ) ade( par[i] , i );
    }

    void init( ) {
        cnt = last = 1;
    }
    void ins( int c ) {
        int cur = ++ cnt;
        len[cur] = len[last] + 1;
        int p = last;
        while( p && !son[p][c] ) son[p][c] = cur , p = par[p];
        if( !p ) par[cur] = 1;
        else {
            int q = son[p][c];
            if( len[q] == len[p] + 1 ) par[cur] = q;
            else {
                int cl = ++ cnt;
                memcpy( son[cl] , son[q] , sizeof son[q] );
                par[cl] = par[q] , len[cl] = len[p] + 1;
                par[q] = par[cur] = cl;
                while( p ) { if( son[p][c] == q ) son[p][c] = cl; p = par[p]; }
            }
        }
        last = cur , lst[++ ps] = cur;
        add( rt[cur] , 1 , n , ps );
    }
    int G[MAXN][19];
    int work( int u , int ln ) {
        for( int k = 18 ; k >= 0 ; -- k )
            if( len[G[u][k]] >= ln ) u = G[u][k];
        return u;
    }
    void dfs( int u , int fa ) {
        for( int i = head[u] ; i ; i = nex[i] ) {
            int v = to[i];
            if( v == fa ) continue;
            G[v][0] = u;
            for( int k = 1 ; k < 19 ; ++ k )
                if( G[G[v][k-1]][k-1] ) G[v][k] = G[G[v][k-1]][k-1];
                else break;
            dfs( v , u );
            rt[u] = merge( rt[u] , rt[v] , 1 , n );
        }
    }
}

int main() {
	//freopen("7.in","r",stdin);
	//freopen("ot","w",stdout);
    cin >> n >> m;
    scanf("%s",ch + 1);
    using namespace solve;
    init( );
    for( int i = 1 ; i <= n ; ++ i )
        ins( ch[i] - 'a' );
    addall( );
    dfs( 1 , 1 );
    for( int i = 1 , a , b , c , d ; i <= m ; ++ i ) {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        int l = 1 , r = d - c + 1;
        while( l <= r ) {
            int mid = l + r >> 1;
            int t = work( lst[c + mid - 1] , mid );
            if( que( rt[t] , 1 , n , a + mid - 1 , b ) ) l = mid + 1;
            else r = mid - 1;
        }
        printf("%d\n",r);
    }
}

posted @ 2020-01-22 22:06  yijan  阅读(93)  评论(0编辑  收藏  举报