Rabin-Karp 字符串匹配算法

复赛比完了就继续研究些算法呗,然后觉得自己字符串匹配还不会,就去啃了啃算法导论,觉得Rabin-Karp挺有意思的就写篇Blog记录一下。
Rabin-Karp算法是Rabin与Karp所提出的字符串匹配算法,其可以引申出二维的模式匹配,运行时间为O((n-m+1)m),但在平均条件下其时间复杂度还是可以接受的。
Rabin-Karp算法的原理是在给定文本T中寻找模式P,将T看做一个d进制的数字,这样每一个与P等长度的T的子序列都会有一个属于自己的值,由于P也有同样的一个值,所以与P的值相等的子序列必定与P相同,从而匹配出模式在文本中出现的次数与位置。
但是P的长度过长可能会导致在存储对应值时爆范围,所以我们要找到一个较大的质数q,将每一个值都mod q,从而避免出现爆范围的情况。但这又带来了新的问题,因为膜过的数可能会相同导致误判。所以每当匹配到一个值与模式P的值相同时,我们称其为一次“伪命中”。这时我们再挨个比对P与所找到的子序列判断是否匹配,虽然会增大时间复杂度,但由于当2个值不等是字符串必定不匹配,所以要比挨个枚举效率高。同时,如果增大质数q的值,则产生“伪命中”的次数会降低,也可以相对增加效率。
伪代码如下:

Rabin-Karp-Matcher(T,P,d,q) //T为文本,P为模式,d为基数,素数q同上文的q
n=T.length
m=P.length
h=d^(m-1) mod q //用于转移
p=0
t=0
for i=1 to m //预处理
    p=(d*p+P[i])mod q
    t=(d*t+T[i])mod q
for s=0 to n-m //匹配
    if p==t
        if P[1..m]==T[s+1..s+m] //比对
            /*do something*/
    if s //转移t
        t=(d*(t-T[s+1]*h)+T[s+m+1])mod q

但是,我们可以尽量避免伪命中点的产生。比如,我们可以设置多个不同的素数q,从而形成多个不同的p值与t值,如果每一个值都成功的对应上了,我们就可以认为这是一次成功的匹配。这样子可以节省效率,但是相应的如果选择不好q的值可能会导致无法排除伪命中点 的错误产生,所以,请三思而后行

你可能感兴趣的:(算法笔记)