KMP算法
- 前缀与后缀的理解
字符串 abcdab
前缀的集合:{a,ab,abc,abcd,abcda}
后缀的集合:{b,ab,dab,cdab,bcdab}
注意:后缀不是从后面写起。
所以next的数组就是不匹配串的前面的前缀与后缀相等的数目就是移动的数目。
而nextval在next的基础上改进,就是在next的数组上加1.
前缀、后缀和部分匹配值。
前缀指除最后一个字符以外,字符串的所有头部子串;
后缀指除第一个字符外,字符串的所有尾部子串;
部分匹配值则为字符串的前缀和后缀的最大公共前后缀长度。
下面以′ababa ′为例进行说明:
- ′a ′的前缀和后缀都为空集最大公共前后缀长度长度为0。
- ′ab ′ 的前缀为{ a } ,后缀为{ b } , { a } ∩ { b } = N U L L,最大公共前后缀长度长度为0。
- ′aba ′的前缀为{ a , ab }, 后缀为{ a , ba } , { a , ab } ∩ { a , ba } = { a }, 最大公共前后缀长度长度为1。
- ′abab ′的前缀∩后缀,{ a , ab , aba } ∩ { b , ab ,bab } = { ab } ,最大公共前后缀长度长度为2。
- ′ababa ′的前缀∩后缀, { a , ab , aba , abab } ∩ { a , ba , aba , baba } = { a , aba } , 公共元素有两个,
最大公共前后缀长度长度为3。
故字符串′ ababa ′ 的最大公共前后缀长度为00123。
移动位数=已匹配的字符数−对应最大公共前后缀长度(最后一个匹配字符)
使用部分匹配值时,每当匹配失败,就去找它前一个元素的部分匹配值,这样使用起来有些不方便,所以将PM表右移一位,
这样哪个元素匹配失败,直接看它自己的部分匹配值即可。将上例中字符串′ abac ′ 的PM表右移一位,就得到了next数组:
有时为了使公式更加简洁、计算简单,将next数组整体+1。因此,next数组就变成:
就是这样一步一步的推next就可以了。
求nextval得先求出next值
第一位为0,然后从左到右,计算哪一位,就把哪一位的next值所对应的值和本位相比较,若不同,nextval值就为本身的next值;若相同,nextval值就为next值所对应的nextval值。
- 第一个为0。
- 计算第二位:第二位b的next值为1,把1对应的a和b比较,不同,则第二位的nextval值为本身的next值,为1。
- 计算第三位:第三位a的next值为1,把1对应的a和a比较,相同,则第三位的nextval值为第一位的nextval值,为0。
- 计算第四位,第四位b的next值为2,把2对应的b和b比较,相同,则第四位的nextval值为第二位的nextval值,为1。
- 计算第五位:第五位a的next值为3,把3对应的a和a比较,相同,则第五位的nextval值为第三位的nextval值,为0。
- 计算第六位:第六位a的next值为4,把4对应的b和a比较,不同,则第六位的nextval值为本身的next值,为4。
- 计算第七位:第七位a的next值为2,把2对应的b和a比较,不同,则第七位的nextval值为本身的next值,为2。
- 计算第八位:第八位b的next值为2,把2对应的b和b比较,相同,则第八位的nextval值为第二位的nextval值,为1。
- 计算第九位:第九位a的next值为3,把3对应的a和a比较,相同,则第九位的nextval值为第三位的nextval值,为0。
- …
总结: 第一位是0,这个记住就好了。第二位的next的值是1,所以与序号为1的比较,如果字母不相同,那么它的nextval值为本身的next值(1).
第三位的next的值是1,所以与序号为1的比较,如果字母相同,那么它的nextval值为序号为1的nextval值.
回溯就是倒回去,重新匹配,但是在KMP算法中是不会回溯的,因为有前后缀,直接跳就可以了。
简单匹配模式下,最坏的时间复杂度就是mn。