KMP算法

关于KMP算法,看了很多博客,自己也做了一些字符串匹配之后,总算弄懂一些了,但是可能还要进一步深入研究,先写一部分吧,这部分足够应对笔试的nextval和next问题了。

关于如何求next:

先给出一个字符串“ababaabab”

    j         1  2  3  4  5  6  7  8  9

    i         a  b  a  b  a  a  b  a  b

  next     0  1  1  2  3  4  2  3  4

nextval   0  1  0  1  1  4  1  0  1

next怎么求呢:

首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

其实在我看来,撇开这个next值在KMP中的正式含义,如果要便于理解,你可以看做后缀中跟前缀的最长共有元素,我们要求的就是这个共有元素加上轮到的字符,得到“最终串”,然后看“最终串”有多长(其实就是看最后一位的位置,变相求字符串长度),就是所求的next值。

关键是理解我说的共有元素,要理解我说的通俗的共有元素,就要看懂上面对前缀和后缀以及“部分匹配值”,所以先看懂啥是共有元素后,再继续往下,我相信你一定能学会。

我个人是很讨厌各种专业名词混杂的博客的,十分不方便理解,所以我尽量用通俗的方式来解释。

我们先看到第一个字符a,此时j=1,求的是next(1)。next(1)为该字符串的第一个字符,它之前没有别的字符,所以不用比较,所以为0。

接着是轮到第二个字符b, 此时j=2,求的是next(2)。next(2)为该字符串的第二个字符,它之前的字符串为“a”,a中没有重复的字符,所以共有元素为空,那么加上轮到的字符“b”,得到的“最终串”为“b”,长度为1,且,那next(2)= 1。

总之,不管前两位是什么,next(1)、next(2)都固定为 0、1。

轮到第三个字符a了,它前面是字符串“ab”,很明显字符串“ab”中不含重复字符,所以共有元素为空,那么加上轮到的字符“a”,得到的“最终串”为“a”,长度为1,且,那next(3)= 1。

轮到第四个字符b了,它前面是字符串“aba”,很明显字符串“aba”中含重复字符,共有元素为“a”,那么加上轮到的字符“a”,得到的“最终串”为“b”,长度为2,且,那next(4)= 2。

轮到第五个字符a了,它前面是字符串“abab”,很明显字符串“abab”中含重复字符,共有元素为“ab”,那么加上轮到的字符“a”,得到的“最终串”为“aba”,长度为3,且,那next(5)= 3。

轮到第六个字符a了,它前面是字符串“ababa”,这时,你要是不理解上面的前缀后缀,我觉得你就嗝屁了,字符串“ababa”存不存在共有元素呢?有的话,是什么呢?"ababa"的前缀为[a, ab aba, abab],后缀为[a,ba,aba,baba],这下看得出来了吧,共有元素取最长是“aba”,加上第六个字符a,最终串为“abaa”,所以next(6)= 4。

以此类推,应该没问题吧,关键是搞懂前缀和后缀。

 

那么next讲完,我们再来说说nextval,看了贼多关于nextval的求法,真的脑袋疼,最后我找到了一个我特别喜欢的,这里跟大家分享一下:

关键在于只看前面有重复字母的几位就可以。

比如上面这个字符串ababaabab吗,我们复制粘贴一下,这样方便大家看:

先给出一个字符串“ababaabab”

    j         1  2  3  4  5  6  7  8  9

    i         a  b  a  b  a  a  b  a  b

  next     0  1  1  2  3  4  2  3  4

nextval   0  1  0  1  1  4  1  0  1

这里我们已经能熟练求出next了,这时候其实工程已经完成一大半了,接下来就是我跟你们说的关键——只看前面有重复字母的,其余跟next一样:

可见从第三位开始都有重复字母,那么前两位nextval就是0、1了,好,我们开始从第三位开始求:

第三位是“a”,它的next值为1,就找字符串的第1位字符,是“a”,一样,那就把这个a的nextval值改为后面这个“a”的next值 0 。

第四位是“b”,它的next值为2,就找字符串的第2位字符,是“b”,一样,那就把这个a的nextval值改为后面这个“b”的next值 1 。

第五位是“a”,它的next值为3,就找字符串的第3位字符,是“a”,但第三位的这个a之前做过处理,所以就把第五位的这个a的nextval值改为第一个“a”的next值 0。

第六位是“a”,它的next值为4,就找字符串的第4位字符,是“b”,诶!不一样,这时候就把nextval等于next就行,所以第六位的next值 为4 。

以此类推。

 

最后是KMP算法的原理,就是KMP匹配串如何移动,每次移动多少的问题。这一块我觉得有个博客写的比我好很多,在这里贴出来,我觉得写的已经十分通俗易懂了。关于前缀后缀和共有元素部分我也略有借鉴,感谢这位博主。

http://www.cnblogs.com/c-cloud/p/3224788.html

posted @ 2018-10-08 17:19  Misakikure  阅读(167)  评论(0编辑  收藏  举报