UVA 10829 L-Gap Substrings

题意:

形如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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值