前言:KMP算法原理理解容易但是代码实现很难理解,所以对每一段代码会使用很长的解释用来助于理解下面开始正题!
KMP 案例分解(图解说明)
S4-算法-KMP算法【2021-02-06】 - 简书 (jianshu.com)
KMP算法的原理如上,本章节具体讲解代码实现。
一、kmpNext数组实现代码:
public static int[] kmpNext(String dest){
int[] next=new int[dest.length()];
next[0]=0;
for (int i = 1,j=0; i < dest.length(); i++) {
while (j>0&&dest.charAt(i)!=dest.charAt(j)){
j=next[j-1];
}
//
if (dest.charAt(i)==dest.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}
1、我们对要对 String str2 = "ABABA" 实现生成next数组的下标。首先进行next数组的初始化,
int[] next=new int[dest.length()]; next[0]=0;
2、对数组进行遍历使用双指针i,j将str2里的字符进行对比识别。
(1)
(2)
(3)
(4)
next数组已经出来了,next=[0, 0, 1, 2, 3]
二、kmpSeach数组实现代码:
public static int kmpSearch(String str1,String str2,int[] next){
for (int i = 0, j = 0; i < str1.length ( ); i++) {
// 情况分两种,一是不匹配到,要回溯;二是匹配到 索引 +1
// 难点:情况一:当dest.charAt(i) !=dest.charAt(j),则需要 next[j-1] 得到新的 j
while (j > 0 && str1.charAt (i) != str2.charAt (j)) {
//这一步十分的精髓,当str1.charAt (i) != str2.charAt (j)就开始让j为下一次循环做准备
j = next[j - 1];
}
// 情况二:匹配到,匹配值数量 +1
if (str1.charAt (i) == str2.charAt (j)) {
j++;
}
// 找到了
if (j == str2.length ( )) {
return i - j + 1;
}
}
return -1;
}
1、str1与str2字符串在通过i,j进行比对,当比对不上时进入 j = next[j - 1],再次比对,为什么是进入next[j-1]的下标呢,我们可以发现。i=6时,j=2时,不相等,j=next[j-1],j=0。i=7与j=0继续相比。如果是传统的暴力查找,我们要重新从i=5,j=0开始重新查找,不难发现步骤多了很多。
三、根据kmpSeach数组实现代码我们发现,在str1.charAt (i) != str2.charAt (j)的时候
j = next[j - 1]
这就意味着next[next.length-1]其实是没有用到的,我们可以之间对末尾初始化0,少一次遍历循环。
public static int[] kmpNext(String dest){
int[] next=new int[dest.length()];
//修改后
next[0]=0;
//修改后
next[dest.length()-1]=0;
//修改后
for (int i = 1,j=0; i < dest.length()-1; i++) {
while (j>0&&dest.charAt(i)!=dest.charAt(j)){
j=next[j-1];
}
//
if (dest.charAt(i)==dest.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}
速度提升将近100ms,还是很可观的。
完整代码如下:
import java.util.Arrays;
public class KMPAlgorithm {
public static void main(String[] args) {
String str1 = "ABCDAB ABABACDABDE";
String str2 = "ABABA";
int[] next=kmpNext(str2);
System.out.println("next="+ Arrays.toString(next));
int index=kmpSearch(str1,str2,next);
System.out.println("index="+index);
}
public static int kmpSearch(String str1,String str2,int[] next){
for (int i = 0, j = 0; i < str1.length ( ); i++) {
// 情况分两种,一是不匹配到,要回溯;二是匹配到 索引 +1
// 难点:情况一:当dest.charAt(i) !=dest.charAt(j),则需要 next[j-1] 得到新的 j
while (j > 0 && str1.charAt (i) != str2.charAt (j)) {
//这一步十分的精髓,当str1.charAt (i) != str2.charAt (j)就开始让j为下一次循环做准备
j = next[j - 1];
}
// 情况二:匹配到,匹配值数量 +1
if (str1.charAt (i) == str2.charAt (j)) {
j++;
}
// 找到了
if (j == str2.length ( )) {
return i - j + 1;// +1 因为索引从 0 开始,i 为 主串总长
}
}
return -1;
}
//获取到一个字符串(字串)的部分匹配值
public static int[] kmpNext(String dest){
int[] next=new int[dest.length()];
//修改后
next[0]=0;
//修改后
next[dest.length()-1]=0;
//修改后
for (int i = 1,j=0; i < dest.length()-1; i++) {
while (j>0&&dest.charAt(i)!=dest.charAt(j)){
j=next[j-1];
}
//
if (dest.charAt(i)==dest.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}
}