数据结构定义和算法--字符串匹配BF&RK算法

BF 算法

BF 算法中的 BF 是 Brute Force 的缩写,也叫朴素匹配算法。用一句话来概括,那就是,我们在主串中,检查起始位置分别是 0、1、2....n-m 且长度为 m 的 n-m+1 个子串,看有没有跟模式串匹配的。

数据结构定义和算法--字符串匹配BF&RK算法_第1张图片

在极端情况下,我们每次都比对 m 个字符,要比对 n-m+1 次,所以,这种算法的最坏情况时间复杂度是 O(n*m)。 但在实际的开发中,它却是一个比较常用的字符串匹配算法。原因有两点:

第一,实际的软件开发中,大部分情况下,模式串和主串的长度都不会太长。大部分情况下,算法执行效率要比这个高很多。

第二,朴素字符串匹配算法思想简单,代码实现也非常简单。在工程中,在满足性能要求的前提下,简单是首选。这也是我们常说的KISS(Keep it Simple and Stupid)设计原则。

RK 算法

RK 算法的全称叫 Rabin-Karp 算法,是由它的两位发明者 Rabin 和 Karp 的名字来命名的。通过哈希算法对主串中的 n-m+1 个子串分别求哈希值,然后逐个与模式串的哈希值比较大小。哈希值是一个数字,数字之间比较是否相等是非常快速的。

数据结构定义和算法--字符串匹配BF&RK算法_第2张图片

哈希算法设计

假设要匹配的字符串的字符集中只包含 K 个字符,我们可以用一个 K 进制数来表示一个子串,这个 K 进制数转化成十进制数,作为子串的哈希值。 

数据结构定义和算法--字符串匹配BF&RK算法_第3张图片

这种哈希算法有一个特点,在主串中,相邻两个子串的哈希值的计算公式有一定关系。

数据结构定义和算法--字符串匹配BF&RK算法_第4张图片

事先计算好 26^0、26^1、26^2……26^(m-1),并且存储在一个长度为 m 的数组中,公式中的“次方”就对应数组的下标。当我们需要计算 26 的 x 次方的时候,就可以从数组的下标为 x 的位置取值,省去了计算的时间。 

数据结构定义和算法--字符串匹配BF&RK算法_第5张图片

 整个 RK 算法包含两部分,计算子串哈希值和模式串哈希值与子串哈希值之间的比较。第一部分,可以通过设计特殊的哈希算法,只需要扫描一遍主串就能计算出所有子串的哈希值了,所以这部分的时间复杂度是 O(n)。模式串哈希值与每个子串哈希值之间的比较的时间复杂度是 O(1),总共需要比较 n-m+1 个子串的哈希值,所以,这部分的时间复杂度也是 O(n)。所以,RK 算法整体的时间复杂度就是 O(n)。

散列冲突/哈希冲突

K进制哈希算法不会出现哈希冲突,但是哈希值容易超出整数范围。我们可以通过设计哈希值较小的算法来避开这个问题,比如我们可以把字符串中每个字母对应的数字相加作为哈希值,或者每一个字母从小到大对应一个素数后相加。

实际上,解决方法很简单。当我们发现一个子串的哈希值跟模式串的哈希值相等的时候,我们只需要再对比一下子串和模式串本身就好了。

《数据结构与算法之美》 -- 王争

你可能感兴趣的:(数据结构和算法)