您的位置:首页 > 其它

UVA 10829 L-Gap Substrings

2016-05-08 11:34 435 查看
题意:

形如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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: