[字符串匹配算法] Rolling Hash ADT and Karp-Rabin Algorithm

本文介绍了Rolling Hash ADT,一种支持在O(1)时间内进行append和skip操作的抽象数据类型。通过哈希值计算和数据结构设计,可以高效地处理字符串操作。接着,文章详细讲解了Karp-Rabin算法,利用Rolling Hash ADT实现在字符串中查找子串的快速匹配,避免了不必要的全比较。文中还提及了计算模逆的几种方法,如欧几里得算法和费马小定理。

1 Rolling Hash ADT

S 代表一个字符串,S 的哈希值(hash value)是 Rolling Hash ADT 的状态量(state), 该ADT支持以下的操作(Operation):

  • hash(S) : 表示合适的哈希函数(hash function), 用于计算 S 的哈希值(hash value).
  • append(c): 将字母 c 连接(append)到字符串 S 的末尾,并计算出新字符串的哈希值(hash value), 这个操作要求在 O(1) 时间内完成
  • skip(c) : 假设字符串 S 以字母 c 开头, skip(c) 将字母 c 从字符串 S 的开头去掉,并计算出新字符串的哈希值(hash value), 这个操作要求在 O(1) 时间内完成

2 Data Structure

实现以上的抽象数据型(ADT)的数据结构如下:

  • hash(x)=u mode p , 其中 u 是字符串 x 对应的数值(例如采用ASCII码转换方法, 字符串”abc”的数值为 979899), p 是接近于 232 的大素数。
  • append(c): (ua+val(c))mod p=[(u modp)a+val(c)]mod p , 其中 a 为字符转换为数值时候的基数,例如,采取ASCII码方式时候基数为256。
  • 为了增加计算效率, 将 u mod p a|x| 缓存
  • skip(c): [uval(c)(a|x1|mod p)]mod p=[(u mod p)val(c)(a|x1|mod p)]mod p
  • 有了缓存值,append() 和 skip() 方法的时间复杂度均为 O(1)

3 Data Structure 的伪代码实现

__init__(base, p):
  hash = 0
  magic = base^0 mod p = 1
  //ibase 可以有多种实现方法:
  //欧几里得法,费马小定理,或者调用gmpy2.invert()函数
  //在后面给出示例代码
  ibase = modular multiplicative inverse of base mode p

append(new):
  hash = (hash * base + new) mod p
  magic = (magic * base) mode p

skip(old):
  hash = (hash - (old * magic) + p * base) mod p
  magic = (magic * ibase) mod p

注:在 skip() 方法中计算 hash 值时候,加上额外的 (p * base)是为了防止(hash - old * magic)的值小于零, 如果上式小于零,在一些编程语言中求mod p后会出现负值, 实际上求mod后的值应该始终在 0 到 p 之间,所以增加 (p * base)。 加上这项并不影响结果,因为 (p * base) mod p = 0。

3.1 计算modular multiplicative inverse的方法 [1] (python)

3.1.1 欧几里得算法
def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m
3.1.2 费马小定理
y = x**(p-2) mod p  # Pseudocode
3.1.3 gmpy module
>>> import gmpy2
>>> gmpy2.invert(0,5)

4 Karp-Rabin算法

Karp-Rabin算法使用Rolling Hash ADT实现了高效的字符串匹配, 伪代码如下:

for c in s: rs.append(c)
for c in t[:len(s)]: rt.append(c)
if rs() == rt(): ...

for i in range(len(s), len(t)):
    rt.skip(t[i-len(s)])
    rt.append(t[i])
    if rs() == rt(): ...

其中,需要在 t 字符串中查找 s 字符串。 rs 与 rt 分别代表两个不同的 rolling hash ADT. 当rs() == rt()时候,需要判断两个字符串是否真正相等, 避免哈希值相同而实际字符串却不相同的情况。


Reference

[1]http://stackoverflow.com/questions/4798654/modular-multiplicative-inverse-function-in-python
[2]MIT OpenCouresware 6.006 Introduction to Algorithm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值