字符串模式匹配----KMP算法(视频讲解)

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

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表示存在且长度为11表示存在且长度为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个常用算法

猜你喜欢

转载自blog.csdn.net/hnyjwang/article/details/80950180