字符串匹配算法之RK

该篇为字符串匹配算法的第二篇,我们讲RK算法
RK算法的全称叫Rabin-Karp算法,是由它的两位发明者Rabin和Karp的名字来命名的。

描述

RK算法的思路是这样的:我们通过哈希算法对主串中的n-m+1个子串分别求哈希值,然后逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相等,那就说明对应的子串和模式串匹配了(这里先不考虑哈希冲突的问题,后面我们会讲到)。因为哈希值是一个数字,数字之间比较是否相等是非常快速的,所以模式串和子串比较的效率就提高了。
不过为了提高计算哈希的计算速率,我们使用上一个哈希的值来求下一个哈希值,假设我们的字符串中只有a-z26个字母,则有下面的公式:
字符串匹配算法之RK_第1张图片
其实该公式对于其他进制是通用的。

实现

class RabinKarp:
    def __init__(self, text):
        self.text = text
        self.d = 256 # 256进制
        self.q = 29 # 取模

    def _matched(self, text, pattern, i, j):
        for d in range(len(pattern)):
            if text[i + d] != pattern[j + d]:
                return False
        return True

    def find(self, pattern):
        text = self.text
        if not pattern or not text:
            return -1
        plen = len(pattern)
        tlen = len(text)
        pHashValue = 0
        tHashValue = 0
        h = 1
        for i in range(plen-1):
            h = (h*self.d) % self.q
        for i in range(plen):
            pHashValue = (self.d * pHashValue + ord(pattern[i])) % self.q
            tHashValue = (self.d * tHashValue + ord(text[i])) % self.q

        for i in range(tlen-plen+1):
            if pHashValue == tHashValue:
                if self._matched(text, pattern, i, 0):
                    return i
                else:
                    return -1
            if i < tlen -plen:
                tHashValue = (self.d * (tHashValue - ord(text[i]) * h) + ord(text[i+plen])) % self.q
                if tHashValue < 0:
                    tHashValue += self.q
        return -1

if __name__ == '__main__':
    matcher = RabinKarp("hello world")
    res = matcher.find("or")
    assert res == 7
    assert matcher.find("ld") == 9

分析

整个RK算法包含两部分,计算子串哈希值和模式串哈希值与子串哈希值之间的比较。第一部分,我们前面也分析了,可以通过设计特殊的哈希算法,只需要扫描一遍主串就能计算出所有子串的哈希值了,所以这部分的时间复杂度O(n)。
模式串哈希值与每个子串哈希值之间的比较的时间复杂度是O(1),总共需要比较n-m+1个子串的哈希值,所以,这部分的时间复杂度也是O(n)。所以,RK算法整体的时间复杂度就是O(n)。但如果存在哈希冲突的情况下,时间复杂度可能会退化。极端情况下,哈希算法大量冲突,时间复杂度就退化为O(n*m)。

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