本人是个菜鸟,最近正在看大话数据结构,正好看到了KMP算法部分,看书中的解释刚开始是懵逼的,后来百度了一些博客,还是一知半解的状态,最后结合了阮一峰老师的讲解,算是搞明白了到底KMP算法next数组大概是怎么回事。下面附上连接:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
由于本人是学的java,大话数据结构中的代码是c,看了两三天,于是动手讲c代码翻译成了java代码。
下面贴上代码:
public class KmpTest {
public static void main(String[] args) {
String S = "aaaabaax";
String T = "aax";
int i = indexOf(S, T);
System.out.println(i);
String aaaav = "ababaaaba";
int[] nextVal = getNext(aaaav);
for (int j :
nextVal) {
System.out.printf(j+",");
}
}
private static int indexOf(String S, String T){
char[] strs = S.toCharArray();
char[] maths = T.toCharArray();
int[] next = getNextVal(T);
int i =0;
int j =0;
while (i< S.length() && j < T.length()){
if (strs[i] == maths[j]){
j++;
i++;
}else{
if(j==0){
i++;
continue;
}
j = next[j-1];
}
}
if(j == T.length()){
return i - j;
}
return -1;
}
/**
* 根据匹配串获取对应的获取next数组
*
* 这里的判断条件思路:
* 根据kmp算法。
* next[0]一定是0,因为没有字符可以比较,由于int数组初始化时,数组中的每个数据都是0,我就省略了next[0]=0的代码。
* 当next[1]时,只有一个字符可以比较,所以next[1]=1,
*
* 为什么比较的时候时比较chars[i-1] ===chars[j-1]呢
*
* 假设当前的模式串为abcabd
* 根据next[0]=0,所以我的i是从1开始的。
* 在还没开始遍历前的变量值为 next:{0} i=0,j=0;
* 第一次进入循环,判断到j==0 所以就i++,j++操作,此时 i=1,j=1
* next[1] = 1,此时变量的值分别为 next:{0,1} i=1,j=1;
* 第二次进入循环j不等于0了,就判断当前的字符串和上次计算出来的j位置的前一个位置的值进行比较。
* 这里就是关键,每次计算出一个next的值,就知道了之前的前后缀的相似度。只需要将当前char[i]的值一直和上次的j值的前一位进行比较
* 当比较不得不相同时,就回溯当前的j值,一旦回溯到了1,就不用回溯了。直接赋值为1了。
* @param T
* @return
*/
private static int[] getNext(String T){
char[] chars = T.toCharArray();
int i = 0;
int j = 0;
// 根据字符串长度初始一个空的next数据
int[] next = new int[chars.length];
// 开始遍历字符串T
while (i < chars.length-1){
if (j==0 || chars[i] == chars[j-1]){
i++;
j++;
next[i] = j;
}else{
j = next[j-1];
}
}
return next;
}
/**
* 因为kmp算法是有缺陷的,aaaaax的next数组为{0,1,2,3,4,5},匹配到x的时候失败了,会回到5,再回到4。。一直回到0,中间是可以省略的
* 所以有的大佬想出了nextVal数组
*
* nextVal的实现基本跟上面的next差不多,只不过在赋值之前,先判断下次要判断逻辑,如果下一次判断的结果和这一次相同,那么就认为
* 这个可能出现了aaaa的情况,所以本次的值为上一次计算的j值
*
* @param T
* @return
*/
private static int[] getNextVal(String T){
char[] chars = T.toCharArray();
int i = 0;
int j = 0;
int[] nextVal = new int[chars.length];
while (i < chars.length-1){
if (j==0 || chars[i] == chars[j-1]){
i++;
j++;
if(chars[i] == chars[j-1]){
nextVal[i] = nextVal[j-1];
}else{
nextVal[i] = j;
}
}else{
j = nextVal[i];
}
}
return nextVal;
}
}