- 与暴力匹配最显著的区别是,在进行串的匹配时,主串P,模式串为S,某个字符不匹配的时候,i不变,j=next[j],从而避免的i指针的回溯,节省了时间开销
- 对于next数组:当模式串中第j个字符匹配失败时,由前1~j-1个字符组成的串记为S,则next[j] = S 的最长相等前后子串的长度 + 1
图解如下:
挡第j个字符匹配失时,我们尝试将模式串后移,那么,移动多少呢?这时候,我们考虑前后缀的概念,如图,j=7时匹配失败,我们把前1~(k-1)个字符看做串S,实际上给,我们要做的,就是把最长前缀移动至最长后缀,此时,i不变,j=next[j]=S的最长前后缀长度+1=5
- 手算next数组
我们知道:
next[1] = 0
next[2] = 1
KMP算法原理很容易理解,下面看一下Java实现:
/**
* @program: algorithm_learn
* @description: KMP算法实现
* @author: Mr.Luo
* @create: 2020-06-08 16:45
*/
public class Kmp_imp {
public static void main(String[] args) {
Kmp_imp kmp = new Kmp_imp();
String a = "abababcdef";
String b = "abcd";
int[] next = kmp.get_next(b.toCharArray());
for(int i = 0; i < next.length; i++){
System.out.println(b.charAt(i)+" "+next[i]);
}
int res = kmp.Kmp(a, b);
System.out.println(res);
}
public int[] get_next(char[] T){
int pLen = T.length;
int[] next = new int[pLen];
int i = 0;
int j = -1;
next[0] = -1;
while (i < T.length-1){//注意i < T.length-1,因为++i之后才开始算next[i]
if (j==-1 || T[i]==T[j]){
++i;
++j;
next[i] = j;
}else {
j=next[j];
}
}
return next;
}
public int Kmp(String source, String pattern){
int i = 0, j = 0;
char[] src = source.toCharArray();
char[] ptn = pattern.toCharArray();
int sLen = src.length;
int pLen = ptn.length;
int[] next = get_next(ptn);
while (i < sLen && j < pLen){
if (j == -1 || src[i] == ptn[j]){
++i;
++j;
}else {
j = next[j];
}
}
if (j == pLen){
return i - j;
}
return -1;
}
}
输出结果:
a -1
b 0
c 0
d 0
4
表示从主串下标为4开始匹配
注意:代码中,下标均从0开始,而分析中的j从1开始,即代码中的next[j]相当于分析中,j和next[j]同步-1