给定一个长度为 n 的01串,有 m 个询问,每个询问 [L,R] 询问所有结束位置在 [L,R] 的前缀,两两之间的最长公共后缀是多长。
Data Constraint
n,m≤100000
显然可以将串反过来构SA。
现在考虑莫队,对于左右指针在同一个块中的答案,单独处理。
假设当前处理的这些询问左端点都落在 [L,R] 这个区间中,然后设 gi 表示 [R,i] 的最大答案。然后左边的端点可以暴力做。
现在问题是如何快速求出在询问区间中,和一个后缀相邻的后缀是谁。这个可以用链表实现。
时间复杂度: O(nn√)
#include
#include
#include
#include
#include
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 ;
}
以上.