在网络上看了很多优秀的博客和百科,KMP算法往往比较难理解。它是一种字符串匹配非常高效的算法,时间复杂度由O(n*m)变为O(n+m)。接下来我尝试着用简单的语言描述我对KMP算法的理解以及next和nextval的求法。
首先先来介绍一下next,next其实是一个数组,用来存储对应位置子串需要后移的位数。
例如 abdadabd,在这个子串中需要去计算它的每一位的next值。
每个位置的next值如图,那么是如何求得的呢?
首先我们要先来了解一下前缀和后缀的概念,前缀指的是包括首字符且不包括最后一个字符的所有子串,后缀指的是包括最后一个字符且不包括首字符的所有子串。
例如"girfriend"这一串字符串,
其前缀指的是
{g,gi,gir,girf,girfr,girfri,girfrie,girfrien}中的元素,
而后缀指的是
{d,nd,end,iend,riend,friend,rfriend,irfriend}中的元素。
在明白前缀和后缀的概念之后,next的值其实就是前缀和后缀集合中最长相同元素子串的字符数。
在上例"abdadabd"中,
'ab'前缀为{a},后缀为{b},其没有相同元素,故next[1]= 0(下标从0开始),
'abd'前缀为{a,ab},后缀为{d,bd},其没有相同元素,故next[2] = 0,
'abda'前缀为{a,ab,abd},后缀为{a,da,bda},有相同元素a,字符数为1,所以next[3] = 0,
后以此类推,直到最后一位
'abdadabd'前缀为{a,ab,abd,abda,abdad,abdada,abdadab},后缀为{d,bd,abd,dabd,adabd,dadabd,bdadabd},可以看出其最长相同元素为'abd',字符数为3,所以next[7] = 3.
计算完next值后,我们根据公式进行子串匹配
移动位数 = 已匹配的字符数 - 对应的next值
首先逐位进行比较
找到不同之后根据公式后移3位(3-0 = 3);
发现依然不匹配,且首字符不匹配,则继续后移1位
同理继续后移一位
发现找到子串。
看到这里,应该对KMP算法的理解更深了。接下来提出一个问题,假设子串为aaaaabbbd呢?next的值应是012340000,假设主串为aaadvdfsdfsdfsdf
子串需要后移1位
在这里我们可以发现浪费了时间进行了没必要的后移,实际上第一次比较时第四位不匹配而前三位是相同字符,后移时可以将子串直接进行后移3位
由此我们得到next优化后nextval的求法
1.第一位的nextval值必定为0,第二位如果于第一位相同则为0,如果不同则为1。
2.第三位的next值为2,那么将第三位和第二位进行比较,均为a,相同,则继续将第二位与第一位进行比较,相同则为0。
3.第四位的next值为3,那么将第四位和第三位进行比较,相同,那么将第三位和第二位进行比较,均为a,相同,则继续将第二位与第一位进行比较,相同则为0。
以此类推。
相同情况时时将该位与该位对应的next值的位数进行比较,直到比较到第一位或不同,不同则值为该位next值,比较到第一位相同为0,不同为1。