题目:
在一个字符串s中,找到第一个与给定的字符串t想匹配的位置
递归求next数组
/**
* 递归求next数组
* <p>
* 原理:若求next[k+1],其实分两种情况:
* <p>
* 目前已知 next[k],假设 next[k] = m ;
* <p>
* 1. 若t[m] == t[k], 则 next[k] = m+1;
* <p>
* 2. 若t[m] != t[k],则 已知 next[m] 的值,假设为 next[m]=d;
* <p>
* 则说明:在数组下标从0到m的子数组前后最长子串的长度为d
* <p>
* 因此,又分为两种情况:
* <p>
* a. 若 t[d] == t[k], 则 next[k] = d+1 ;
* <p>
* b. 若 t[d] != t[k], 则 m = d,回到 2
* <p>
* 事实上,上述步骤 a 和 b 只不过是将步骤 1 和 2 中的 m 换成 d 的重复
*/
private static int[] getNext1(String[] t) {
int[] next = new int[t.length];
next[0] = -1;
int m = -1;
// 当前计算的是next[i+1]的值,故i从0到t.length-1
int i = 0;
while (i < t.length - 1) {
if (m == -1 || t[m] == t[i]) {
// 从next[i]到next[i+1]实际步长只能是0和1两个中的一个,故最多+1
m++;
i++;
next[i] = m;
} else {
m = next[m];
}
}
return next;
}
m = next[m]的由来:
由上述代码分析可知:
若求next[k+1],其实分两种情况:
目前已知 next[k],假设 next[k] = m 。
1. 若t[m] == t[k], 则 next[k] = m+1;
2. 若t[m] != t[k],则 已知 next[m] 的值,假设为 next[m]=d;
则现在所求的最长前后缀串 出现在 数组下标从 0 到 d 和 k-d+1到 k-d 中,循环 步骤 1;分析:每次求的d = next[ next[k] ], 然后将 d 的值赋给 m 重复 1,即此时的 m= next[next[k]],而由 步骤1 可知 m = next[k],故 m 的值没此都是 对next[k] 求next[]的值,因此在递归过程中只需让:m = next[m]循环即可
测试代码:
```
public class KMP {
public static void main(String[] args) {
String[] s = new String[] { "a", "b", "c", "a", "b", "a", "b", "c", "a" };// 主串
String[] t = { "a", "b", "a", "b" };// 待匹配的串
// 非递归求next数组:
// int[] next = getNext(t);
// 递归求next数组:
int[] next = getNext1(t);
System.out.print("next数组为:");
for (int i : next) {
System.out.print(i + " ");
}
getResult(s, t, next);
}
private static void getResult(String[] s, String[] t, int[] next) {
int i = 0;
int j = 0;
while (i < s.length && j < t.length) {
if (next[j] == -1 || s[i] == t[j]) {
i++;
j++;
} else {
j = next[j];
}
if (j == t.length - 1) {
if (s[i] == t[j]) {
System.out.println("\n匹配串在主串中首次出现的位置为:" + (i - j));
} else {
System.out.println("\n匹配失败");
}
break;
}
}
}
运行结果:
next数组为:-1 0 0 1
匹配串在主串中首次出现的位置为:3