一、介绍
传送门: 参考资料
二、实例:
1、分析思路图解:
2、总结KMP步骤:
- 先得到子串的部分匹配表(最长公共前后缀)
- 再使用部分匹配表完成KMP匹配
三、代码实操
1、得到子串的部分匹配表
再往上面填写,即可得到子串的部分匹配表(最长公共前后缀)。
关于下面这行代码的由来:
如下:
首先,len - 1 ,然后 它这个位置上的数字,就是新的len
这里的 1 代表匹配到的最长公共串的长度为1.
2、使用部分匹配表完成KMP匹配
测试:
输出:
完整代码:
package com.huey.kmp;
import java.util.Arrays;
public class KMPAlgorithm {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str1 = "BBC ABCDAB ABCDABCDABDE";
String str2 = "ABCDABD";
// ABCDAB ->[0, 0, 0, 0, 1, 2]
// 前缀:A AB ABC ABCD ABCDA 后缀:BCDAB CDAB DAB AB B
// 这个串有共有元素"AB",长度为2。所以,最后一个元素为2.
int[] next = kmpNext("ABCDABD");// [0, 0, 0, 0, 1, 2, 0]
// 前缀:A AB ABC ABCD ABCDA ABCDAB 后缀:BCDABD CDABD DABD ABD BD D
// 这个串没有共有元素,所以最后一个为0.
System.out.println(Arrays.toString(next));
int index = kmpSearch(str1, str2, next);
System.out.println("index = " + index);
}
// 写出KMP算法
/**
* @param str1 源字符串
* @param str2 子串
* @param next 部分匹配表,是子串对应的部分匹配表
* @return 若是-1就是没有匹配到,否则返回第一个匹配的位置
*/
public static int kmpSearch(String str1, String str2, int[] next) {
// 遍历
for (int i = 0, j = 0; i < str1.length(); i++) {// i指向str1,j指向str2
// 需要处理 str1.charAt(i) != str2.charAt(j),去调整j 的大小
// KMP算法核心点
while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
j = next[j - 1];
}
if (str1.charAt(i) == str2.charAt(j)) {
j++;
}
if (j == str2.length()) {// 找到了
return i - j + 1;
}
}
return -1;
}
// 1.获取到一个字符串(子串)的部分匹配值
public static int[] kmpNext(String dest) {
// 创建一个next 数组保存部分匹配值
int[] next = new int[dest.length()];
next[0] = 0;// 长度为1时,部分匹配值就是0
for (int i = 1, j = 0; i < dest.length(); i++) {// i表示字符串后缀,j表示字符串前缀
// 当dest.charAt(i) != dest.charAt(j)时,我们需要从next[j-1]获取新的j
// 直到我们发现有dest.charAt(i) != dest.charAt(j)成立才退出
while (j > 0 && dest.charAt(i) != dest.charAt(j)) {// 这是KMP算法的核心点
j = next[j - 1];
}
// 当dest.charAt(i) == dest.charAt(j)时,部分匹配值就是要+1
if (dest.charAt(i) == dest.charAt(j)) {// charAt() 方法用于返回指定索引处的字符
j++;
}
next[i] = j;
}
return next;
}
}