KMP算法推导

来源:互联网 发布:淘宝名字大全2016款女 编辑:程序博客网 时间:2024/06/16 11:44

一个问题

有一个主串S1,一个子串S2,如何判断主串S1中是否存在子串S2 ?

朴素模式匹配算法

我们举个例子

主串S1:abcdefgab
子串S2:abcdex

朴素模式匹配法的原理:

设i,j 分别表示主串S1与子串S2上的第i,j 个元素。i,j 均初始化为1

1. S1的第i个元素 与 S2的第j个元素进行对比,如果相同,执行步骤 2 。如果不同,执行步骤 3
2. i++, j++ , 重复步骤 1
3. j = 1 , 执行步骤 1
。。。
直到找到子串 或 主串被遍历

观察朴素模式匹配过程:

1.
这里写图片描述
2.
![此处输入图片的描述][2]
3.
![此处输入图片的描述][3]
4.
这里写图片描述
5.
![此处输入图片的描述][5]
6.
![此处输入图片的描述][6]
朴素模式匹配的步骤为1~6

我们观察一下子串S2:abcdex,首字符a与后边的bcdex中的字符均不相等
当第1步前边的abcde已匹配,而S1中的f 与 S2中的f 不匹配,执行了第2步
由于之前b已经与S1中的第二个元素匹配,b与a不相等,则没必要有第二步

同理,步骤3 4 5均不必要

或许会觉得其实也没什么。。。

假如主串为:00000000000000000000000000000000000000000000000000000000001
子串为:00000000000000001
朴素模式匹配法将会耗费大量时间,效率低

KMP匹配算法

正如上边所说,我们应该省去一些不必要的匹配,这也正是KMP算法产生的根本原因

我们可以在发现f不与x匹配时,由步骤1直接跳到步骤6

因为子串abcdex前边的abcde中,a与他们均不相同

假如子串后遍也有a呢?

我么再来看个例子:
主串S1:abcabcabc
子串S2:abcabx

朴素模式匹配法步骤如下:
1.
这里写图片描述
2.
这里写图片描述
3.
这里写图片描述
4.
这里写图片描述
5.
这里写图片描述
6.
这里写图片描述

我们可以跳过步骤2 和步骤3 ,原因就不多说了
然后观察步骤4 步骤5

在子串中第一位的a与第四位的a相等,第二位与第五位同为b
由于步骤1中已经匹配了前5 个元素,所以步骤4 步骤5 也可以省去,不需要再比较了
因为肯定也是相等的(之前比较过了,还判断什么)

最后结果:只需要步骤1 与 步骤6

一个发现

经过一番推到后的匹配步骤中
- 在步骤1 时,主串的下标i 为6
- 在步骤6 中,主串的下标i 还是6

朴素模式匹配中,主串的下标i 是不断地回溯的
经过我们的推导,发现这种回溯是不必要

我们的KMP算法就是为了让这些没必要的回溯不发生

如何实现KMP算法呢?

既然主串的i 不需要回溯,那么我们就要考虑子串下标j 的变化了
在我们之前的两个例子中,屡屡提到子串中首字符与后边字符的比较
如果发现有相等,则j 就会有不同的变化
所以j 的变化与主串无关,而与子串中的重复有关

有什么关系呢?

比如第一个例子中,子串为:abcdex
没有任何的重复,所以j直接由6 变为1

第二个例子中,子串为:abcabcx
前缀ab 与 后边x 之前的串的后缀ab相同
则j 由6变为了3

规律:j 的变化取决于当前字符之前的串的前后缀的相似程度

一个让人看着发迷的公式

我们把串各个位置的j 值的变化定义为一个数组next,next长度为子串的长度
然后我们有了这样的定义:
这里写图片描述


先写到这儿,稍后写next数组的推导 :)

0 0