字符串匹配(KMP)算法

字符串匹配(KMP)算法

给你两个字符串,寻找其中一个字符串是否包含另一个字符串,如果包含,返回包含的起始位置。
如下面两个字符串:

String str = "BBC ABCDAB ABCDABCDABDE";
String ptr = "ABCDABD";

算法说明

一般匹配字符串时,我们从目标字符串str(假设长度为n)的第一个下标选取和ptr长度(长度为m)一样的子字符串进行比较,如果一样,就返回开始处的下标值,不一样,选取str下一个下标,同样选取长度为n的字符串进行比较,直到str的末尾(实际比较时,下标移动到n-m)。这样的时间复杂度是O(n*m)。

KMP算法:可以实现复杂度为O(m+n)

为何简化了时间复杂度:
充分利用了目标字符串ptr的性质(比如里面部分字符串的重复性,即使不存在重复字段,在比较时,实现最大的移动量)。

next数组

这里我们要计算一个长度为m的转移函数next。

next数组的含义就是一个固定字符串的最长前缀和最长后缀相同的长度。

比如:abcjkdabc,那么这个数组的最长前缀和最长后缀相同必然是abc。
cbcbc,最长前缀和最长后缀相同是cbc。
abcbc,最长前缀和最长后缀相同是不存在的。

** 注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。
比如aaaa相同的最长前缀和最长后缀是aaa。**
对于目标字符串ptr,ABCDABD,长度是7,所以next[0],next[1],next[2],next[3],next[4],next[5],next[6]分别计算的是
A,AB,ABC,ABCD,ABCDA,ABCDAB,ABCDABD的相同的最长前缀和最长后缀的长度。由于A,AB,ABC,ABCD,ABCDA,ABCDAB,ABCDABD的相同的最长前缀和最长后缀是“”,“”,“”,“”,“A”,“AB”,“”,所以next数组的值是[-1,-1,-1,-1,0,1,-1],这里-1表示不存在,0表示存在长度为1,2表示存在长度为3
《字符串匹配(KMP)算法》

算法流程

1.
《字符串匹配(KMP)算法》
首先,字符串”BBC ABCDAB ABCDABCDABDE”的第一个字符与搜索词”ABCDABD”的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。
2.
《字符串匹配(KMP)算法》
因为B与A不匹配,搜索词再往后移。
3.
《字符串匹配(KMP)算法》
就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。
4.
《字符串匹配(KMP)算法》
接着比较字符串和搜索词的下一个字符,还是相同。
5.
《字符串匹配(KMP)算法》
直到字符串有一个字符,与搜索词对应的字符不相同为止。
6.
《字符串匹配(KMP)算法》
这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把”搜索位置”移到已经比较过的位置,重比一遍。
7.
《字符串匹配(KMP)算法》
一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是”ABCDAB”。KMP算法的想法是,设法利用这个已知信息,不要把”搜索位置”移回已经比较过的位置,继续把它向后移,这样就提高了效率。
8.
《字符串匹配(KMP)算法》
怎么做到这一点呢?可以针对搜索词,之前算出的next数组就有作用了

9.
《字符串匹配(KMP)算法》
已知空格与D不匹配时,前面六个字符”ABCDAB”是匹配的。查表可知,最后一个匹配字符B对应的”部分匹配值”为2,因此按照下面的公式算出向后移动的位数:

移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 – 2 等于4,所以将搜索词向后移动4位。
10.
《字符串匹配(KMP)算法》
因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2(”AB”),对应的”部分匹配值”为0。所以,移动位数 = 2 – 0,结果为 2,于是将搜索词向后移2位。
11.
《字符串匹配(KMP)算法》
因为空格与A不匹配,继续后移一位。
12.
《字符串匹配(KMP)算法》
逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 – 2,继续将搜索词向后移动4位。
13.
《字符串匹配(KMP)算法》
逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 – 0,再将搜索词向后移动7位,这里就不再重复了。

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/huxiaochang123/article/details/80586717
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞