kmp变形 如何判断第i个字符是否匹配函数 hdu 4749

题目来源:

http://acm.hdu.edu.cn/showproblem.php?pid=4749

题意: 

题意:给出两个数字串A、B。问A中有多少不相交的子串a能匹配B。匹配的意思是a中任意两个位置i和j的大小关系和B的这两个位置的大小关系是一样的。

思路:若是完全一模一样的匹配的话那么KMP是很好理解的。那么对于上述的比较方式怎么比较两个串相等呢?对于a的位置j和B的位置i(j < i),我们比较aa(1,2 ,...j) 与bb(i - j + 1 , ... , i)这两个串中,j之前aa中小于(或 等于 或 大于)a[j] 的个数, 是否  ==   与  i之前bb中小于(或 等于 或大于) b[i] 的个数  。 

一个个匹配过去,如果前缀匹配,并且加上当前字母后,前面比这个字母小的,相等的,大于的数字个数都相等,就是匹配 。

代码如下:

const int Max_N = 100005 ;
int sumt[Max_N][26]  , sump[Max_N][26] ; // sum[i][j]表示从第1位到第i位,有sum[i][j] 个j
int TT[Max_N] , PP[Max_N] , next[Max_N] ;
int n , m , k ;
// 每次匹配时 是
//   (1 ,2 ,... , j)
// 与(i-j + 1 , ... , i)进行匹配
bool  ok1(int i, int j){
    int sma1= 0 , equ1 = 0 ,  sma2 = 0 , equ2 = 0 ;
    int h ;
    for(h = 1 ; h <= k ; h++){
        if(h < PP[i])
            sma1 += sump[i][h] - sump[i - j][h] ;
        else if(h == PP[i])
            equ1 += sump[i][h] - sump[i - j][h] ;
        if(h < PP[j])
            sma2 += sump[j][h] ;
        else if(h == PP[j])
            equ2 += sump[j][h] ;
    }
    return (sma1 == sma2) && (equ1 == equ2) ;
}
void get_next(){
    int i , k = 0 ;
    next[1] = 0 ;
    for( i = 2 ; i<= m ; i++){
        while(k > 0 && !ok1(i , k + 1))
            k = next[k] ;
        if(ok1(i, k + 1))
            k ++ ;
        next[i] = k ;
    }
}
bool ok2(int i , int j){
    int sma1 = 0 , equ1 = 0 ,  sma2 = 0 , equ2 = 0 ;
    int h ;
    for(h = 1 ; h <= k ; h++){
        if(h < TT[i])
            sma1 += sumt[i][h] - sumt[i - j][h] ;
        else if(h == TT[i])
            equ1 += sumt[i][h] - sumt[i - j][h] ;
        if(h < PP[j])
            sma2 += sump[j][h] ;
        else if(h == PP[j])
            equ2 += sump[j][h] ;
    }
    return (sma1 == sma2) && (equ1 == equ2) ;
}
int kmp(){
    int i , k = 0 ;
    int cnt = 0 ;
    for(i = 1 ; i<= n ; i++){
        while(k > 0 && !ok2(i , k + 1))
            k = next[k] ;
        if(ok2(i , k + 1))
            k ++ ;
        if(k == m){
            cnt ++ ;
            k = 0 ; // 特殊处理, 字串不相交
        }
    }
    return cnt ;
}
int main(){
    while(scanf("%d%d%d" , &n ,&m ,&k) != EOF){
        int i , j ;
        memset(sumt , 0 , sizeof(sumt)) ;
        memset(sump , 0 , sizeof(sump)) ;
        for(i = 1 ; i<= n ; i++){
             scanf("%d" , &TT[i]) ;
            for(j = 1 ; j<= k ; j++)  sumt[i][j] = sumt[i - 1][j] ;
                sumt[i][TT[i]] ++ ;
        }
        for(i = 1 ; i<= m ; i++){
            scanf("%d" , &PP[i]) ;
            for(j = 1 ; j <= k ; j++) sump[i][j] = sump[i - 1][j] ;
            sump[i][PP[i]] ++ ;
        }
        get_next() ;
        printf("%d\n" , kmp()) ;
    }
    return 0 ;
}

 

转载于:https://www.cnblogs.com/zn505119020/p/3738512.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值