问题描述
字符串匹配问题可以归纳为如下的问题:
在长度为n的文本T[1...n]中,查找一个长度为m的模式P[1...m]。并且假设T,P中的元素都来自一个有限字母集合Ʃ。如果存在位移s,其中0≤s≤n-m,使得T[s+1..s+m] = P[1..m]。则可以认为模式P在T中出现过。
1. 朴素算法
最简单的字符串匹配算法是朴素算法。该算法最直观,通过遍历文本T,对每一个可能的位移s都比较T[s+1..s+m]于P[1..m]是否匹配。
代码实现
代码用python写的:
def naive_string_match(T, P):
n = len(T)
m = len(P)
for s in range(0, n-m+1):
k = 0
for i in range(0, m):
if T[s+i] != P[i]:
break
else:
k += 1
if k == m:
print s
算法分析
最坏情况下,对每一个s都需要做m次(模式P的长度为m)的比较。则算法的上届是O((n-m+1)*m)。到后面我们会看到朴素算法之所以慢,是因为它只是关心有效的位移,而忽略其它无效的位移。当一次位移s被验证是无效的之后,它只是向右位移1位,然后从头开始继续下一次的比较。这样做完全没有利用到之前已经匹配的信息,而这些信息有时候会很有用。
2. Rabin-Karp算法
对朴素算法的一个简单的改进就是Rabin-Karp算法。Rabin-Karp算法的思路是将字符串的比较转换成数字的比较。比较两个长度为m的字符串是否相等需要O(m)的时间,而比较两个数字是否相等通常可以是Ɵ(1)。
为了将字符串映射到对应的数字,我们需要用到哈希函数。我们都知道开放寻址法的哈希函数(open addressing)是可能遇到冲突的。对于这个问题来说冲突意味着虽然两个字符串的哈希值是一样的,但是这两个字符串实际上是不一样的。
解决的办法是当遇到哈希值相同时,再做m次(模式P的长度为m)遍历,近一步判断这两个字符串是否相等。既是说,哈希值是第一步地判断,如果两个字符串不相等那么他们的哈希值也肯定不相等。通过第一步的筛选后,再做近一步更可靠的筛选。
运气好的话,大部分不匹配的字符串会在第一步(通过哈希值)被筛选掉,仅留有少量的字符串需要近一步的审查。
代码实现
继续附上Python代码:
'''
想最快的入门Python吗?请搜索:"泉小朵",来学习Python最快入门教程。
也可以加入我们的Python学习Q群:902936549,看看前辈们是如何学习的。
'''
def rabin_karp_matcher(T, P):
n = len(T)
m = len(P)
h1 = hash(P)
for s in range(0, n-m+1):
h2 = hash(T[s:s+m])
if h1 != h2:
continue
else:
k = 0
for i in range(0, m):
if T[s+i] != P[i]:
break
else:
k += 1