茶艺师学算法打卡12:BF/RK字符串匹配算法
学习笔记
BF算法
全名为 Brute Force ,暴力匹配算法。
简直简单粗暴,主串长度为 n ,模式串长度为 m ,就直接硬来比较 次,直到找到匹配的部分为止。
时间复杂度自然是 ,但别嫌弃它耗时久,它不仅简单好用(好写),在小规模的字符串匹配中,BF 算法比别的高级匹配算法表现还好。
RK算法
全名为 Rabin Karp 匹配算法,就是 Rabin 与 Karp 这两人一起发明的算法。
这算法的一句话总结,就是 BF 算法 + 哈希表结合。
这里体现为两点。
一,在比较 次主串子串与模式串时,通过哈希算法,把两者都换算为“哈希值”,再比较两“哈希值”是否相等。
二、这 次主串子串可以一次算完,用一个数组将其系列结果存起来,到时要比较,直接通过下标找出算好的答案,这分明就是哈希表的应用。
这里附上简单的代码示意(主体思路一致,但在细节处理上更偏向我的习惯)
// 主串 "Hello"
// 模式串 "ll"
n, m := 5, 2
s1 = " " + "Hello"
s2 = " " + "ll"
s1Hash, p131 := make([]int64, n + 1), make([]int64, m + 1)
s1Hash[0], p131[0] = 0, 1
var s2Hash int64
// 计算主串的哈希值
for i := 1; i <= n; i++ {
s1Hash[i] = s1Hash[i - 1] * 131 + int64(s1[i]) - 'a' + 1
p131[i] = p131[i - 1] * 131
}
// 计算模式串的哈希值
for i := 1; i <= m; i++ {
s2Hash = s2Hash * 131 + int64(s2[i]) - 'a' + 1
}
for i := m; i <= n; i++ {
if cal(s1Hash, p131, i - m + 1, i) == s2Hash {
// 找到匹配
// 当然可以再次验证两个子窜是否相等
}
}
// 快速计算任一子串的哈希值
func cal(s1Hash, p131 []int64, i, j int) int64 {
return s1Hash[j] - s1Hash[i - 1] * p131[j - i + 1]
}
时间复杂度为 ,基本就是算主串的哈希值耗时。
当然,如果哈希函数设计得不好,经常引发哈希碰撞,RK 算法的耗时还是会退化为 ,也就是 BF 算法的水平了。