kmp算法详解-上
算法简介
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度]O(m+n)。
kmp算法的核心就是利用匹配串的前缀表来做失败后匹配串的回溯,从而保证主串不回溯。减少匹配次数。
关于前缀表
- 前缀:以第一个字符开头、不包含最后一个字符的所有连续子串均称为前缀;
- 后缀:以最后一个字符结尾、不包含第一个字符的所有连续子串均称为后缀;
- 最长相等(公共)前后缀:前缀子串 = 后缀子串 且 长度最长;
- 前缀表:记录下标i之前(包括i)的字符串的最长相等前后缀的长度。
有点干巴,举个例子。
abacab
这个字符串对应的前缀表为:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
字符串 | a | ab | aba | abac | abaca | abacab |
最长相等前后缀 | a | a | ab | |||
最长相等前后缀长度 | 0 | 0 | 1 | 0 | 1 | 2 |
前缀表的使用
前缀表是用来回溯匹配串用的,下面我们看一个例子:
- 主串:
ababahop
- 匹配串:
abah
首先我们求一下匹配串的前缀表
下标 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
字符串 | a | ab | aba | abah |
最长相等前后缀 | a | |||
最长相等前后缀长度 | 0 | 0 | 1 | 0 |
使用i
遍历主串,使用j
遍历匹配串,
1、如果i
和j
指示的字符相等则i
,j
均后移。
2、当移动到下标3的时候,主串和匹配串字符不相等,则i
不动,j
回溯到next[2]的位置,也就是我们前面求得的前缀表下标位2的值,此时j
等于1,重复步骤一。
3、j
移动到字符串末尾,说明找到了匹配串在主串中的位置。
为什么是前缀表
很多博主讲到这里就开始讲如何计算前缀表了,你是否有想过为什么匹配串一定要回溯到next[j-1]的位置呢,回溯到其他位置不行吗?理论依据是什么?
1、假设主串和匹配串下标移动到某一个位置,出现字符不相等。此时按照kmp算法匹配串下标j
需要回溯,我们我们把主串叫做haystack,匹配串叫做pattern,此时haystack[0~
i] = pattern[0~
j],即蓝色部分。
2、假设匹配串j回溯到某一位置,使得匹配串和主串重新匹配,长度为m,即橙黄色部分。
3、由于我们知道蓝色部分是相等的,所以我们将他们平移对齐,做一个比较,会发现,此时haystack[0~
m] = haystack[i-m~
i]也可以说成是pattern[0~
m] = pattern[i-m~
i],即前缀等于后缀。由此可知匹配失败后匹配串下标回溯位置就是当前字符前面子串的前缀和后缀相等的位置m,即next[j-1]。
结束语
下一章我们讲一下如何计算前缀表,以及kmp算法如何进行优化。