JSOI2013编程作业

大致问题是给你两个串,S与T,要求求S有多少子串与T匹配。匹配定义为两个串大写字母位置及内容一模一样,小写字母是可替换的。详见样例

Input

3 //数据组数

AiBjCiDECjDiFGC

AaBiCaDECiDaFGC

cDEcDEbDE

aDEbDE

ccddef

aab

Output

1

1

2

这题可以用Hash解决
对于小写字母,我们记录一个小写字母与上一个和它相同的小写字母的距离la[],和下一个与他相同的小写字母的距离nx[]。
先把T串的nx[]给hash起来。
再在S串线性扫一遍,维护长度为|T|的子串hash值。即加入一个新小写字母要将他上一个与他相同小写字母在hash值里的位置赋上值。
大写字母的hash值就是MAX_nx[]~MAX_nx[]+26

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std ;

typedef  long long ull ;

int Q , i , j , k , n , m , l[2] ;

ull  hs[2][3] , mo[3] = { 128760641,100000081 , 1196033  } ;

char s[2][1000010] ;

struct node {
    int la , nx ;// all record posi of changing 
}d[2][1000010] ;

int w[27] ;

void Preprocess( int p , int n ) {
    int i , j , k ;
    memset( w , 255 , sizeof w ) ;
    memset( d[p] , 0 , sizeof d[p] ) ;
    for( i=0 ; i<n ; i++ ) {
        if( s[p][i] > 'Z' ) {
            if(  w[ s[p][i] - 'a' ]!=-1 ) d[p][i].la = i - w[ s[p][i] - 'a' ] , 
                d[p][  w[ s[p][i] - 'a' ] ].nx = i -  w[ s[p][i] - 'a' ] ;
            w[ s[p][i] - 'a' ] = i ;
        }
    }
}

// We communi that 0 ~ l[1]-1 is lowercase letter posi changing , while l[1]+25  uppercase letter . And we should x( l[1]+26 )

ull Eraser[1000010] , X ;

void Hashize( int p , int n ) {
    int i , j , k ;
    ull ret[3]  ;
    memset( ret , 0 , sizeof ret ) ;
    for( i=0 ; i<n ; i++ ) {
        for( k=0 ; k<3 ; k++ ) ret[k] = ret[k] * X  % mo[k] ;
        if( s[p][i] > 'Z' ) {
            if( i + d[p][i].nx < n ) {
                for( k=0 ; k<3 ; k++ ) ret[k] = ( ret[k] + d[p][i].nx ) % mo[k] ;
                Eraser[i] = d[p][i].nx  ;
            }
        } else {
            for( k=0 ; k<3 ; k++ ) ret[k] = ( ret[k] + l[1] + s[p][i] - 'A' ) % mo[k] ;
            Eraser[i] = l[1] + s[p][i] - 'A' ;
        }
    }
    for( k=0 ; k<3 ; k++ ) hs[p][k] = ret[k] ;
}

ull inx[1000010][3] ;

int main() { 
    scanf("%d" , &Q ) ;
    while( Q-- ) {
        scanf("%s%s",s[0] , s[1] ) ;
        l[0] = strlen( s[0] ) , l[1] = strlen( s[1] ) ;
        Preprocess( 0 , l[0] ) ;
        Preprocess( 1 , l[1] ) ;
        X = ( l[1] + 26 ) ;
        Hashize( 1 , l[1] ) ;
        memset( Eraser , 0 , sizeof Eraser ) ;
        Hashize( 0 , l[1] ) ;
        for( inx[0][0] = inx[0][1] = inx[0][2]  = 1 , i = 1 ; i<=l[0] ; i++ ) 
            for( k=0 ; k<3 ; k++ ) inx[i][k] = inx[i-1][k] * X % mo[k];
        int ans = 0 ;
        if( hs[0][0]==hs[1][0] && hs[0][1]==hs[1][1] && hs[0][2]==hs[1][2] ) ans ++ ;
        for( i=l[1] ; i<l[0] ; i++ ) {
            for( k=0 ; k<3 ; k++ ) hs[0][k] = ( ( hs[0][k] - inx[ l[1]-1 ][k] * Eraser[ i-l[1] ] ) % mo[k] + mo[k] ) % mo[k];
            for( k=0 ; k<3 ; k++ ) hs[0][k] = hs[0][k] * X % mo[k];
            if( s[0][i]  >'Z' ) {
                if( d[0][i].la<l[1] ) {
                    for( k=0 ; k<3 ; k++ ) hs[0][k] = ( hs[0][k] + ( Eraser[ i-d[0][i].la ] = d[0][i].la ) * inx[ d[0][i].la ][k] ) % mo[k] ;
                }
            } else {
                for( k=0 ; k<3 ; k++ ) hs[0][k] = ( hs[0][k] + ( Eraser[i] = ( s[0][i] - 'A' + l[1] ) ) ) % mo[k] ;
            }
            if( hs[0][0]==hs[1][0] && hs[0][1]==hs[1][1] && hs[0][2]==hs[1][2] ) ans ++ ;
        }
        printf("%d\n",ans ) ;
    }
}

DebugLog
带取模的哈希值用 unsigned long long 存 ,溢出会错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值