建议观看整个详解视频。
KMP算法详解1:https://www.bilibili.com/video/BV1vR4y1j7aL/
KMP算法详解2:https://www.bilibili.com/video/BV1Lr4y1h7Dv/
KMP算法详解3:https://www.bilibili.com/video/BV1LZ4y1o7aQ/
/**
*
*
* KMP 详解3:next表代码实现。
*
*
*/
public class KMP {
public static int[] next(String pattern) {
// 1. 暴力递归
// char[] chars = pattern.toCharArray();
// int[] next = new int[chars.length];
// for (int i = 0; i < chars.length; i ++) {
// next[i] = recursion(chars, i);
// }
// return next;
// 2. 递归 + 备忘录(记忆搜索)
// char[] chars = pattern.toCharArray();
// int[] next = new int[chars.length];
// // 初始化,next[0] = -1;next[1] = 0;next[2+] = -2;
// next[0] = -1;
// for (int i = 2; i < chars.length; i++) {
// next[i] = -2;
// memorandum(chars, i, next);
// }
// return next;
// 3. 迭代
return dp3(pattern);
}
//
// 截止到index之前的串(即p[index]子串)的最长相同前后缀 递归
// index 对应求解 i+1
private static int recursion(char[] p, int index) {
if (index < 2) {
return index - 1; // 0:-1,1:0
}
int i = index - 1;
int n = i;
while (true) {
n = recursion(p, n);
// 1. 在某一次循环中得到结果退出。
if (p[n] == p[i]) {
return n + 1;
}
//2. 前部分缩减到0,最后是比较p[0] 与 p[i],如果相等,结果则为1,否则为0。
if (n == 0) {
return 0;
}
}
}
// 递归+备忘录
private static int memorandum(char[] p, int index, int[] next) {
if (next[index] != -2) {
return next[index];
}
int i = index - 1;
int n = i;
while (true) {
n = memorandum(p, n, next);
// 1. 在某一次循环中得到结果退出。
if (p[n] == p[i]) {
next[index] = n + 1;
return next[index];
}
//2. 前部分缩减到0,最后是比较p[i] 与 p[0],如果相等,结果则为1,否则为0。
if (n == 0) {
next[index] = 0;
return next[index];
}
}
}
// 迭代
private static int[] dp(String pattern) {
char[] p = pattern.toCharArray();
int length = p.length;
int[] next = new int[length];
next[0] = -1;
if (length <= 2) {
return next;
}
//
int index = 2;
int n = next[index - 1];
while (index < length) {
if (n < 0 || p[index-1] == p[n]) {
next[index] = n + 1;
index ++;
n = next[index - 1];// next[index++]=++n
continue;
}
// 可用小技巧:因为 next[0]=-1,next[1]=0, 可令两个条件合二为一
// 当n=0时,继续走 n=next[n](即 n=-1),那么 next[index++]=0(=++n)
// if (n == 0) {
// next[index] = 0;
// index ++;
// // n = next[index - 1] (=0)
// continue;
// }
// 逐一递减的方式查找最长相同前后缀!
n = next[n];
}
return next;
}
// 迭代
private static int[] dp2(String pattern) {
char[] p = pattern.toCharArray();
int length = p.length;
int[] next = new int[length];
next[0] = -1;
if (length <= 2) {
return next;
}
int index = 2;
int n = next[index - 1];
while (index < length) {
// 这里总是要减一,有没有办法优化呢?
// index=index-1,即index从1开始,注意index++要改++index,且条件要改成index<length-1,:dp3
if (n < 0 || p[index-1] == p[n]) {
next[index++] = ++n;
} else {
// 逐一递减的方式查找最长相同前后缀!
n = next[n];
}
}
return next;
}
// 迭代
private static int[] dp3(String pattern) {
char[] p = pattern.toCharArray();
int length = p.length;
int[] next = new int[length];
next[0] = -1;
if (length <= 2) {
return next;
}
int index = 1;
int n = next[index];
int iMax = length - 1;
while (index < iMax) {
if (n < 0 || p[index] == p[n]) {
next[++index] = ++n;
} else {
// 逐一递减的方式查找最长相同前后缀!
n = next[n];
}
}
return next;
}
}
单元测试:
public class KMPTest {
@Test
public void next() {
String pattern = "xxaxxxb";
int[] next = KMP.next(pattern);
String result = Arrays.stream(next).mapToObj(i -> i + "").collect(Collectors.joining(" "));
assertEquals("-1 0 1 0 1 2 2", result);
}
}