5027. 歷史行程

題目大意

給定一個長度爲n 的01串,有m 個詢問,每個詢問[L,R] 詢問所有結束位置在[L,R] 的前綴,兩兩之間的最長公共後綴是多長。

Data Constraint
n,m100000

題解

顯然可以將串反過來構SA。
現在考慮莫隊,對於左右指針在同一個塊中的答案,單獨處理。
假設當前處理的這些詢問左端點都落在[L,R] 這個區間中,然後設gi 表示[R,i] 的最大答案。然後左邊的端點可以暴力做。
現在問題是如何快速求出在詢問區間中,和一個後綴相鄰的後綴是誰。這個可以用鏈表實現。

時間複雜度:O(nn)

SRC

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

#define N 100000 + 10
const int MAXN = 18 ;
const int MAXM = 410 ;
struct Query {
    int l , r , h ;
} Q[N] ;

char S[N] , TS[N] ;

int SA[N] , Rank[N] , Height[N] ;
int tax[N] , tp[N] ;

int f[N][MAXN] , Rec[N][MAXM] , g[N] ;
int Pre[N] , Next[N] ;
int Tab[N] , Belong[N] ;
int n , m , maxc = '1' , Block , Cnt ;
int ans[N] ;

bool cmp( Query a , Query b ) { return Belong[a.l] < Belong[b.l] || ( Belong[a.l] == Belong[b.l] && a.r > b.r ) ; }

bool equal( int x , int y , int w ) { return tp[x] == tp[y] && tp[x+w] == tp[y+w] ; }

void Rsort() {
    memset( tax , 0 , sizeof(tax) ) ;
    for (int i = 1 ; i <= n ; i ++ ) tax[Rank[tp[i]]] ++ ;
    for (int i = 1 ; i <= maxc ; i ++ ) tax[i] += tax[i-1] ;
    for (int i = n ; i >= 1 ; i -- ) SA[tax[Rank[tp[i]]]--] = tp[i] ;
}

void Suffix() {
    for (int i = 1 ; i <= n ; i ++ ) Rank[i] = S[i] , tp[i] = i ;
    Rsort() ;
    int p = 1 ;
    for (int w = 1 ; p < n ; maxc = p , w += w ) {
        p = 0 ;
        for (int i = n - w + 1 ; i <= n ; i ++ ) tp[++p] = i ;
        for (int i = 1 ; i <= n ; i ++ ) if ( SA[i] > w ) tp[++p] = SA[i] - w ;
        Rsort() ;
        swap( Rank , tp ) ;
        Rank[SA[1]] = p = 1 ;
        for (int i = 2 ; i <= n ; i ++ ) {
            if ( !equal( SA[i-1] , SA[i] , w ) ) p ++ ;
            Rank[SA[i]] = p ;
        }
    }
    int k = 0 ;
    for (int i = 1 ; i <= n ; i ++ ) {
        if ( k ) k -- ;
        int j = SA[Rank[i]-1] ;
        while ( S[i+k] == S[j+k] ) k ++ ;
        Height[Rank[i]] = k ;
    }
}

int Find( int l , int r ) {
    if ( l > r ) swap( l , r ) ;
    l ++ ;
    int k = Tab[r-l+1] ;
    return min( f[l][k] , f[r-(1<<(k))+1][k] ) ;
}

void ReSet() {
    for (int i = 1 ; i <= n ; i ++ ) Pre[i] = i - 1 , Next[i] = i + 1 ;
    Next[0] = 1 ;
    Pre[n+1] = n ;
}

void Delete( int x ) {
    Next[Pre[x]] = Next[x] ;
    Pre[Next[x]] = Pre[x] ;
}

int Insert( int x ) {
    int ret = 0 ;
    ret = max( ret , Next[x] <= n ? Find( x , Next[x] ) : 0 ) ;
    ret = max( ret , Pre[x] >= 1 ? Find( Pre[x] , x ) : 0 ) ;
    Next[Pre[x]] = x ;
    Pre[Next[x]] = x ;
    return ret ;
}

int main() {
    freopen( "history.in" , "r" , stdin ) ;
    freopen( "history.out" , "w" , stdout ) ;
    scanf( "%d%d" , &n , &m ) ;
    scanf( "%s" , TS + 1 ) ;
    for (int i = 1 ; i <= n ; i ++ ) S[i] = TS[n-i+1] ;
    Suffix() ;
    for (int i = 2 ; i <= n ; i ++ ) Tab[i] = Tab[i/2] + 1 ;
    for (int i = 1 ; i <= n ; i ++ ) f[i][0] = Height[i] ;
    for (int j = 1 ; j < MAXN ; j ++ ) {
        for (int i = 1 ; i <= n ; i ++ ) {
            f[i][j] = f[i][j-1] ;
            if ( i + (1 << (j - 1)) <= n ) f[i][j] = min( f[i][j] , f[i+(1<<(j-1))][j-1] ) ;
        }
    }
    Block = 350 ;
    for (int i = 1 ; i <= n ; i ++ ) Belong[i] = i / Block + ( i % Block != 0 ) ;
    for (int i = 1 ; i <= n ; i ++ ) {
        for (int j = 1 ; j <= 400 ; j ++ ) {
            Rec[i][j] = max( Rec[i][j-1] , i - j > 0 ? Find( Rank[i-j] , Rank[i] ) : 0 ) ;
        }
    }
    for (int i = 1 ; i <= m ; i ++ ) {
        int l , r ;
        scanf( "%d%d" , &l , &r ) ;
        l = n - l + 1 , r = n - r + 1 ;
        if ( l > r ) swap( l , r ) ;
        if ( r - l + 1 <= 400 ) {
            for (int j = l + 1 ; j <= r ; j ++ ) ans[i] = max( ans[i] , Rec[j][j-l] ) ;
        } else {
            ++ Cnt ;
            Q[Cnt].l = l ;
            Q[Cnt].r = r ;
            Q[Cnt].h = i ;
        }
    }
    sort( Q + 1 , Q + Cnt + 1 , cmp ) ;
    for (int st = 1 ; st <= Cnt ; st ++ ) {
        int ed = st , R = Block * Belong[Q[st].l] , L = R - Block + 1 ;
        ReSet() , g[R] = 0 ;
        while ( ed < Cnt && Belong[Q[ed+1].l] == Belong[Q[st].l] ) ed ++ ;
        for (int i = 1 ; i < R ; i ++ ) Delete( Rank[i] ) ;
        for (int i = n ; i > R ; i -- ) Delete( Rank[i] ) ;
        for (int i = R + 1 ; i <= n ; i ++ ) g[i] = max( g[i-1] , Insert( Rank[i] ) ) ;
        for (int i = R - 1 ; i >= L ; i -- ) Insert( Rank[i] ) ;
        int lasr = n ;
        for (int i = st ; i <= ed ; i ++ ) {
            int now = Q[i].h ;
            while ( Q[i].r < lasr ) Delete( Rank[lasr] ) , lasr -- ;
            ans[now] = g[Q[i].r] ;
            for (int j = L ; j < R ; j ++ ) Delete( Rank[j] ) ;
            for (int j = R - 1 ; j >= L ; j -- ) {
                int ret = Insert( Rank[j] ) ;
                if ( j >= Q[i].l ) ans[now] = max( ans[now] , ret ) ;
            }
        }
        st = ed ;
    }
    for (int i = 1 ; i <= m ; i ++ ) printf( "%d\n" , ans[i] ) ;
    return 0 ;
}

以上.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章