零、前言
为了下面便于说明,先定义两个名词,分别是主串和模式串。在字符串 A 中查找字符串 B,则 A 为主串,B 为模式串。
假设,主串中字符数量为 L1,模式串的字符数量为 L2 。
一、BF 算法
1、基本信息
BF 算法中的 BF 是 Brute Force 的缩写,中文叫作暴力匹配算法,也叫朴素匹配算法。
2、查找流程
从主串第一个位置开始匹配模式串,匹配完成之后,模式串移动一个字符,移动次数为 L1 - L2 + 1 次完成对主串的匹配。
上述过程中可以的优化的点是每次匹配时发现一个字符不一样时就可以中断当前匹配过程直接进入下一个匹配。
3、时间复杂度
单次匹配的次数为 L2,一共 匹配 L1 - L2 + 1,故时间复杂度为 O(L1*L2),即:O(n*m) 。
二、PK 算法
1、基本信息
RK 算法的全称叫 Rabin-Karp 算法,是由它的两位发明者 Rabin 和 Karp 的名字来命名的。
该算法可以理解为 BF 算法的升级版。
设计思路大概为: BF 算法简单但是时间复杂度高,为了解决该问题,可以优化的地方是更改每次匹配时比对的内容,由之前的比对每一个字符改为比对双方的 hash 值,进一步优化 hash 值计算的方法从而降低查找的时间复杂度。
2、查找流程
(1)version 1
将主串拆分成(L1 - L2 + 1)个子串,计算每一个子串的 hash 值;计算模式串的 hash 值。最后用模式串 hash 值与主串的(L1 - L2 + 1)个子串的 hash 值进行对比,相同的即为字符串匹配。
(2)version 2
在版本 1 的思路下继续延伸,计算 hash 值时间依然较多,此处可以优化。可以将 26 个英文字母作为 0、1、2、3、……、25 数字,将字符串转成十进制数字,栗子:
字符串:gced,转换成数字:6 * 26 ^ 3 + 2 * 26 ^ 2 + 4 * 26 ^ 1 + 3 * 26 ^ 0 = 106915 ,暂称该数为神秘数。
上述每一个主串的子串以及模式串均计算出神秘数,对比神秘数即可完成字符串匹配。当然有 神秘数 冲突的情况,每次神秘数比对相同时可以再次对比下两个字符串。
(3)version 3
在版本 2 中,每次计算神秘数的过程依然可以优化,如下所示:
假设主串为 gcedb
字符串:gced,转换成数字:6 * 26 ^ 3 + 2 * 26 ^ 2 + 4 * 26 ^ 1 + 3 * 26 ^ 0
向后挪一位,得到字符串 cedb,转换成数字:2 * 26 ^ 3 + 4 * 26 ^ 2 + 3 * 26 ^ 1 + 1 * 26 ^ 0,
可以发现计算 cedb 的神秘数时,可以写成如下:26*(2 * 26 ^ 2 + 4 * 26 ^ 1 + 3 * 26 ^ 0) + 1 * 26 ^ 0,括号部分的值已经在计算 gced 神秘数的时候得到了,这样每次保存上一个神秘数的一部分计算结果省略了大量的计算过程,可以降低时间复杂度。
3、时间复杂度
一共 匹配 L1 - L2 + 1,故时间复杂度为 O(L1),即:O(n) 。
4、神秘数冲突
当神秘数冲突过多时,实际上 PK 算法就退化成了 BF 算法,因为要频繁的比对子串和模式串,加大的时间复杂度,所以如何计算神秘数是关键,上述的计算过程仅仅是其中之一的方法。
参考:极客时间《数据结构与算法之美》王争
这门课真心推荐,内容很经典、栗子很形象,里面还包含了很多面试题目。真是居家旅行必备良药。
(SAW:Game Over!)