2.4.2、KMP算法
资讯网址:www.qghkt.com
腾讯课堂:https://qghkt.ke.qq.com/20个常用算法
【基本思想】
在布鲁特-福斯算法的基础上,进行如下改进:每当匹配过程中出现相比较的字符不相等时,不需要回溯主串字符的位置指针,而是利用已经得到的“部分匹配”的结果,将模式串向右“滑动”尽可能远的距离,再继续进行比较。
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
【图解过程】
给定主串“ABCDABCDABBABCDABCDABDD”,子串“ABCDABD”。
1)第一趟比较,先A,然后比较BCDABD。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
D |
A |
B |
D |
2)第二趟比较,由于前一趟比较到字符D时,不匹配,KMP算法要求主串不回退位置。所以要子串进行相应的处理,从主串的位置找子串之前可以匹配的“子串”。图示如下:
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
所找到的“子串”为“AB”,应当是可以匹配的最长子串。所以从原子串的AB后进行后面的比较。直至下一个不匹配,即D。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
D |
A |
B |
D |
3)第三趟比较,从原子串中去找之前可以匹配的最长子串,即AB。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
然后再以此往后进行比较CDABD,由于第一个C就不匹配就只能结束本趟,再去找可以匹配最长子串。没有最长子串,那就从原子串的第一个重新开始比较。
4)第四趟比较,第一个字符就不匹配,之前就没有最长子串。下一个从原子串的第一个位置开始比较。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
5)第五趟比较,依次可以比较ABCDAB。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
D |
A |
B |
直至最后一个字符D,不匹配。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
D |
A |
B |
D |
6)第六趟比较,在前一趟的基础上找最长子串,即为AB,从原子串的这个位置后开始比较。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
一直往后比较,直至原子串中的所有字符都被比较后,说明找到了匹配的子串的位置。
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
B |
A |
B |
C |
D |
A |
B |
C |
D |
A |
B |
D |
D |
A |
B |
C |
D |
A |
B |
D |
求找可匹配的最长子串的解决办法:
字符串的前缀:是指从字符串第一个字符开始,到最后一个字符之前(不包含最后一个字符)结束的可能情况。
字符串的后缀:是指从字符串第一个字符之后(不含第一个字符)开始,到最后一个字符结束的可能情况。
字符串 |
可能前缀 |
可能后缀 |
最长公共串 |
长度 |
个数 |
a |
无 |
无 |
无 |
0 |
-1 |
ab |
a |
b |
无 |
0 |
-1 |
aba |
a, ab |
ba, a |
a |
1 |
0 |
abab |
a, ab, aba |
bab, ab, b |
ab |
2 |
1 |
ababa |
a, ab, aba, abab |
baba, aba, ba, a |
aba |
3 |
2 |
-1表示不存在,0表示存在且长度为1,1表示存在且长度为2,……。
给定子串“ABCDABD”,可能的“子串”情况如下
字符串 |
可能的前缀 |
可能的后缀 |
最长串 |
长度 |
数组 |
A |
空 |
空 |
空 |
0 |
-1 |
AB |
A |
B |
空 |
0 |
-1 |
ABC |
A, AB |
BC, C |
空 |
0 |
-1 |
ABCD |
A, AB, ABC |
BCD, CD, D |
空 |
0 |
-1 |
ABCDA |
A, AB, ABC, ABCD |
BCDA, CDA, DA, A |
A |
1 |
0 |
ABCDAB |
A, … , ABCD, ABCDA |
BCDAB,CDAB,DAB…B |
AB |
2 |
1 |
ABCDABD |
A, …, ABCDAB |
BCDABD, …, D |
空 |
0 |
-1 |
A, AB, ABC, ABCD,ABCDA, ABCDAB
BCDABD, CDABD,DABD, ABD, BD, D
【算法代码】
/****************************************************************
* 函数名称:getNext
* 功能描述:得到模式串中的局部匹配信息
* 参数说明:next, 局部匹配信息的结果数组
* sub,模式串
* 返回 值:void
* 作 者:www.qghkt.com
* 创建时间:
*****************************************************************
* Copyright @ 清哥好课堂 All rightsreserved
*****************************************************************/
void getNext(int*next, const char *sub)
{
next[0] = -1;
int k = -1;
int len = strlen1(sub);
for (int i = 1; i < len; i++)
{
while (sub[k+1]!=sub[i] &&k>-1)
{//如果下一个不相同,往前回溯,等于前一个值 。
k = next[k];
}
if (sub[k+1] == sub[i])
{
k = k + 1;
}
next[i] = k;
}
}
/****************************************************************
* 函数名称:searchKMP
* 功能描述:KMP算法的模式匹配
* 参数说明:src, 主串
* sub,模式串
* 返回 值:-1表示没有找到匹配的模式串,非-1的值,表示在主串中的位置
* 作 者:www.qghkt.com
* 创建时间:
*****************************************************************
* Copyright @ 清哥好课堂 All rightsreserved
*****************************************************************/
intsearchKMP(const char *src, const char *sub)
{
int slen = strlen1(src);
int tlen = strlen1(sub);
//得到next
int *next = (int*)malloc(sizeof(int)*tlen);
getNext(next, sub);
//模式匹配
int k = -1;
for (int i = 0; i < slen; i++)
{
//判断当前要不要回退
while (k>-1 && sub[k+1]!= src[i])
{//回溯
k = next[k];
}
if (src[i] == sub[k+1])
{
++k;
}
if (k == tlen - 1)
{
free(next);
next = NULL;
return i - tlen + 1;
}
}
free(next);
next = NULL;
return -1;
}
资讯网址:www.qghkt.com
腾讯课堂:https://qghkt.ke.qq.com/20个常用算法