Karp-Rabin字符串匹配算法

思想:将特定字符串用hash散列的方式映射到一个整数,两串匹配时就不用再逐个字符比较,而是进行整数间的比较,可以在O(1)的时间完成。

假设有文本串t[0, n),模式串p[0, m), n > m 

采用的hash函数:(其中b为一个提前设定好的底数)

 Hash(t[0, m)) = t[0]*b^{m - 1} + t[1]*b^{m-2}+......+t[m-1]*b^{0}

Hash(p[0, m)) = p[0]*b^{m - 1} + p[1]*b^{m-2}+......+p[m-1]*b^{0}

为什么这里只计算t的前m位呢? 因为计算一次hash值需要O(m)的时间,如果在匹配过程中,每次都用循环计算一遍,复杂度将上升到O(m),整体复杂度上升至O(n * m),与蛮力算法相当。但是先计算一次,再采用滚动hash的方式(O(1))来更新t的子串的hash值,就可以把复杂度控制在 O(n + m),即线性范围内

所谓滚动hash,拿 t[0, m)举例,下一个m长度的子串为t[1, m+1),两个子串只有 t[0]和t[m]不同,相当于减去一个 t[0],加回一个 t[m],当然中间部分(t[1 ~ m - 1]要各乘一个b,即使用O(1)时间完成了由hash(t[i,m))到hash(t[i + 1], m + 1) 的转换。

代码如下:

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int B = 256, M = 97, N = 100;     // 3个数都可更改,B为底数,M为散列表长度,N为串规模
char p[N], t[N];

void RabinKarp(char* t, char* p){
    int t_len = strlen(t), p_len = strlen(p);

    //这里要计算 b^(m - 1),因为害怕溢出,所以对M取模
    int h = 1;
    for(int i = 0; i < p_len - 1; i++)
        h = (B * h) % M;

    int t_hash = 0, p_hash = 0;
    //计算前 p_len 个字符的hash
    for(int i = 0; i < p_len; i++){
        t_hash = ( B * t_hash + t[i]) % M;
        p_hash = ( B * p_hash + p[i]) % M;
    }

    for(int i = 0; i <= t_len - p_len; i++){

        //如果hash值一样,再花 O(m)的时间比对值是否相同,因为不同的值可能映射到同一个hash地址(即hash冲突)
        if(t_hash == p_hash && memcmp(t + i, p, p_len) == 0)
            cout << "find index i:" << i << endl;
        
        //滚动更新,O(1)复杂度
        t_hash = ( (t_hash - t[i] * h ) * B + t[i + p_len]) % M;

        //确保hash值始终为正
        if(t_hash < 0)
            t_hash = t_hash + M;
    }


}

int main(){
    strcpy(t, "this is a test, but not just a test");
    strcpy(p, "test");

    RabinKarp(t, p);

    return 0;
}

运行结果:

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值