关于KMP的资料不算少,但是都不是很好理解(个人看法),所以自己整理一下,加深记忆。
KMP算法是解决字符串匹配问题的较高效算法,字符串匹配问题是常见的算法问题。给出两个字符串,分别为母串和子
串,字符串匹配就是为了在母串中找到该子串。
正常的思路是逐个去匹配,也就是下面几个步骤
1.首先用子串的第一个字符去匹配母串的第一个字符
2.若匹配成功,即两个字符相等,则继续匹配下一个,若不等,匹配子串的第一个和母串的第二个
3.假设母串的前五个和子串都相同,当匹配第六个时,不同,那么此时正常的思路是用子串的第一个去匹配母串的第二
个,步骤重复与上一致。当字符串足够长的时候,这个办法效率就会很低。因为这个办法没有用到一个重要的信息,母
串的前五个和子串都是相同的,KMP算法呢就是利用这个信息,推算出一个值X,只需要比较母串的第六个和子串的第
X个即可,不需要反复的比较字符。
这个值叫做部分匹配值,那么每一个子串字符都会对应一个部分匹配值,这些值组成一个数组,也就是常说的Next数组
部分匹配值是如何得到的,也就是怎样求出Next数组,就成为了KMP算法的关键。
求出部分匹配值
先了解部分匹配值是什么,然后再想如何用代码求出来。用一个字符串来解释,“abaabcac” ,这个字符串有8位,每
一位都对应一个匹配值,匹配值就是该位之前的字符串的前缀后缀中共同元素的最大长度。 "前缀"指除了最后一个字
符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。
当N=1,默认匹配值为0;
当N=2,字符串为a,匹配值为1
当N=3,字符串为ab,前缀为a,后缀为b,无公共元素,匹配值为1
当N=4,字符串为aba,前缀为a,ab,后缀为a,ba,公共元素长度为1,匹配加1为2
...
以此类推,此时可能大家会有两个疑惑,为什么匹配值要加1,为什么匹配值计算的是不包括当前这个字符的值
第一,匹配值加1是因为,回到上面那个例子,字母串前五个相同,假设此时Next[6]=3,当子串第六个去和母串的
第六个匹配时,发现并不相同,可以想象是将子串向右滑行最大的距离,假设此时前五个字符为“abcab”,第一二个字
符和第四五个字符相同,那么完全可以将子串向右滑行3个距离,直接用c去和母串的第六个比较,所以匹配值加1。
如果这样还不好理解的话,字符串都是需要遍历比较的(此时先不考虑字符串是从0开始遍历的,后面会提到),用i
遍历母串,j遍历子串,当i=j=6的时候,i=6,j=next[j]=3,这样就不需要再用子串从头遍历了。
那么卫视不包括当前字符是因为,当前的字符是和母串不想同的字符,那么右滑的时候不需要把这个字符考虑进去,
这个字符还是等待进一步比较的,只需要右滑掉首尾最大程度一致的字符即可。
思路理解了以后,下面的代码示例(以Java为例)
代码中就需要注意上面提到的字符串从0遍历的问题了。你可能会发现看完了上面说的一箩筐,即使了解了部分匹配
值是什么,是怎么根据字符串算出来的,但是依旧不会写代码。求部分匹配值,可以看成是对字符串的一种切割,因
为求得的匹配值实际上就是被“切割”下来的字符串的长度。
//样例子串
String match="abaabcac";
//部分匹配值数组,下标从1开始到子串的长
int[] next=new int[match.length()+1];
//默认值
next[1]=0;
int i=1,j=0;
while(i
字符串匹配
字符串的匹配过程其实和求部分匹配值的过程很相似
public class KMP {
public static void main(String[] args) {
//样例母串
String orignal="acabaabaabcacaabc";
//样例子串
String match="abaabcac";
//部分匹配值数组,下标从1开始到子串的长
int[] next=new int[match.length()+1];
//默认值
next[1]=0;
int i=1,j=0;
while(imatch.length()){
System.out.println(i-match.length()-1);//从母串第几个开始
System.out.println(orignal.substring(i-match.length()-1, i-1));
}
}
}
}
好理解了,其实先不需要管下标,下标值总是长度值-1。
参考:(有些细节方面不太一致,但是思路是想通的)
http://blog.csdn.net/yutianzuijin/article/details/11954939/
http://kb.cnblogs.com/page/176818/