题意:
形如UVU这种字符串,其中U、V都是字符串,V的长度为L,那么称此种字符串为L-Gap String,要求的是一个字符串中有多少个子串为L-Gap String。
题解:
网上有很多关于此题的题解,但是都只是说了怎么做的,并没有说为什么是这样。
开始其实也不懂,后面写出来就好像懂了。
大概说一下思路,首先用后缀数组求出lcp是很容易想到的,
我们可以每次枚举U的长度L,那么就可以得到一个区间[i,i+L+G],这里的G是UVU中V的长度,
每次的长度G就只从此区间中取,就可以避免重复计算答案,
如果区间[i,i+L+G]的lcp的长度大于等于L是不是就一种可能,至于大于L就看成L就可以了,因为要保证G在区间内,但是这里i怎么办,直接枚举肯定会TLE,
假设当前L为4,G为1,i为5,在计算区间[5,10]时,如果i每次直接移动L,其实在区间[5+j,10-j],j<=L是没有计算的,如果将这段没计算的区间计算出来,那么i就可以移动L复杂度为nlogn,那么时间就没问题了,
问题其实就相当于求出5~0和10~0的公共前缀,那么就可以求出j为多少,即可以求出U长度为L,间隔为G的方案种数,
因为假设[i,i+L+G]的公共前缀为X<=L,那么j可以向前移动Y次(相当于i-Y和i+L+G-Y的前缀为Y),那么他们的总前缀长度为X+Y,枚举U的长度为L,所以方案数为X+Y-L+1。
求5~0和10~0的公共前缀就可以通过反转求后缀数组,不就可以求出最长公共前缀了。
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cmath> #include <cstdlib> #include <utility> #include <map> #include <set> #include <queue> #include <vector> #include <iostream> using namespace std; #define INF 0x3f3f3f3f #define eps 1e-6 #define CLR( a, v ) memset ( a, v, sizeof ( a ) ) #define LL long long #define DBUG printf ( "here!!!" ) #define rep( i, a, b ) for ( int i = ( a ); i < ( b ); i ++ ) #define PB push_back #define ULL unsigned long long #define PI acos ( -1.0 ) #define lson l, m, rt << 1 #define rson m+1, r, rt << 1 | 1 #define lowbit( x ) ( ( x )&( -x ) ) typedef pair < int, int > Pii; typedef pair < double, double > Pdd; const int maxn = 50005*4; int read_int ( ) { int res = 0; int ch; while ( ( ch = getchar ( ) ) && ! ( ch >= '0' && ch <= '9' ) ) { if ( ch == -1 ) return -1; } while ( ch >= '0' && ch <= '9' ) { res = res*10+( ch-'0' ); ch = getchar ( ); } return res; } char str[maxn]; int t1[maxn], t2[maxn], rk[maxn], sa[maxn]; int lcp[maxn], cnt[maxn]; void build_sa ( int n, int m ) { int * x = t1, * y = t2; for ( int i = 0; i < m; i ++ ) cnt[i] = 0; for ( int i = 0; i < n; i ++ ) cnt[ x[i] = str[i] ] ++; for ( int i = 1; i < m; i ++ ) cnt[i] += cnt[i-1]; for ( int i = n-1; i >= 0; i -- ) sa[ --cnt[ x[i] ] ] = i; for ( int k = 1; k <= n; k <<= 1 ) { int p = 0; for ( int i = n-k; i < n; i ++ ) y[p ++] = i; for ( int i = 0; i < n; i ++ ) if ( sa[i] >= k ) y[p ++] = sa[i]-k; for ( int i = 0; i < m; i ++ ) cnt[i] = 0; for ( int i = 0; i < n; i ++ ) cnt[ x[ y[i] ] ] ++; for ( int i = 1; i < m; i ++ ) cnt[i] += cnt[i-1]; for ( int i = n-1; i >= 0; i -- ) sa[ --cnt[ x[ y[i] ] ] ] = y[i]; swap ( x, y ); x[ sa[0] ] = 0; p = 1; for ( int i = 1; i < n; i ++ ) x[ sa[i] ] = y[ sa[i-1] ] == y[ sa[i] ] && y[ sa[i-1]+k ] == y[ sa[i]+k ] ? p-1 : p ++; if ( p >= n ) break ; m = p; } } void build_lcp ( int n ) { for ( int i = 0; i <= n; i ++ ) rk[ sa[i] ] = i; int h = 0; lcp[0] = 0; for ( int i = 0; i < n; i ++ ) { int j = sa[ rk[i]-1 ]; if ( h ) h --; while ( j+h < n && i+h < n ) { if ( str[j+h] != str[i+h] ) break ; h ++; } lcp[ rk[i] ] = h; } } int mn[maxn][25]; void RMQ ( int n ) { for ( int i = 1; i <= n; i ++ ) mn[i][0] = lcp[i]; for ( int j = 1; ( 1 << j ) <= n; j ++ ) for ( int i = 1; i+( 1 << ( j-1 ) ) <= n; i ++ ) mn[i][j] = min ( mn[i][j-1], mn[ i+( 1 << ( j-1 ) ) ][j-1] ); } int query ( int l, int r ) { int x = rk[l], y = rk[r]; if ( x > y ) swap ( x, y ); x ++; int k = log2 ( y-x+1 ); //注意 return min ( mn[x][k], mn[ y-( 1 << k )+1 ][k] ); } void solve ( ) { int T, g, cas = 1; scanf ( "%d", &T ); while ( T -- ) { scanf ( "%d%s", &g, str ); int len = strlen ( str ); str[len] = 125; for ( int i = len-1; i >= 0; i -- ) str[len+len-i] = str[i]; str[len+len+1] = '\0'; int n = 2*len+1; build_sa ( n+1, 200 ); build_lcp ( n ); RMQ ( n ); LL ans = 0; for ( int L = 1; L < len/2; L ++ ) { for ( int i = 0; i < len; i += L ) { int j = i+L+g; if ( str[i] != str[j] ) continue ; int s = 0; if ( j < len ) s += min ( query ( i, j ), L ); if ( i > 0 ) s += min ( query ( n-i, n-j ), L-1 );//往前最长为L-1 ans += max ( 0, s-L+1 ); } } printf ( "Case %d: %lld\n", cas ++, ans ); } } int main ( ) { solve ( ); return 0; }