KMP算法是用来判断子串是否存在于主串中,KMP算法核心是对子串进行自检,生成next数组,从而和主串进行比较时减少没必要的操作从而提高效率。下面看一个示例,看说明下next数组:
/**
* KMP算法:
* 两个字符串,主串:cfrababaaaba 子串:ababaaaba,判断主串是否包含子串,若包含返回起始下标,否则返回-1
*/
public class KmpAlgorithm {
/**
* 根据给定的子串数组T,自检得到next数组
* @return
*/
public static int[] getNext(char[] T){
//定义两个索引变量,i表示后继,j表示前继
int i=1, j=0;
int[] next = new int[T.length];
next[1] = 0;
while(i< T[0]){
if(j ==0 || T[i] == T[j]){
i++;
j++;
next[i] = j;
}else{
//j 回溯
j = next[j];
}
}
return next;
}
public static int kmp_search(String mainString, String subString){
char[] S = new char[mainString.length()+1];
S[0] = (char)mainString.length();
char[] T = new char[subString.length()+1];
T[0] = (char)subString.length();
int i = 0;
for(char s:mainString.toCharArray()){
i++;
S[i] = s;
}
i = 0;
for(char s:subString.toCharArray()){
i++;
T[i] = s;
}
//得到NEXT数组
int[] next = getNext(T);
i=1;
int j =1;
while(i<=S[0] && j<=T[0]){
if(S[i] == T[j] || j==0){
i++;
j++;
}else{
j = next[j];
}
}
if(j > T[0]){
return i - T[0];
}
return -1;
}
public static void main(String[] args) {
String mainString = "cfrababaaaba";
String subString = "ababaaaba";
int index = kmp_search(mainString, subString);
System.out.println(index);
}
}
输出:4,索引是从1开始的。
在上面的代码中,abaaaba字符串会生成一个数长度为8的next数组,其中第一位为字符串的长度。这个是因为当时学习KMP算法时看的教程是用C语言实现的,所以第一版实现也是按照这个来的,下面我们用JAVA的思想来实现:
package recursion;
/**
* KMP算法
**/
public class Kmp {
public static int[] getNext(char[] T){
int j=-1; //前继
int i=0; //后继
int[] next = new int[T.length];
//next数组第一个元素永远为-1
next[0] = -1;
while(i<T.length-1){
if(j == -1 || T[i] == T[j]){
i++;
j++;
if(T[i] != T[j]){
next[i] = j;
}else{
next[i] = next[j];
}
}else{
//j 回溯
j = next[j];
}
}
return next;
}
public static int kmp_search(String a, String b){
char[] S = a.toCharArray();
char[] T = b.toCharArray();
//得到next数组
int[] next = getNext(T);
int i=0, j=0;
while(i<S.length && j<T.length){
if(j == -1 || S[i] == T[j]){
i++;
j++;
}else{
j = next[j];
}
}
if (j >= T.length){
return i - T.length;
}
return -1;
}
public static void main(String[] args) {
int index = kmp_search("cfrababaaaba", "ababaaaba");
System.out.println(index);
}
}
结果:3,索引是从0开始的