说KMP前说一下String类型的indexOf方法:
- indexOf所用到的底层是KMP算法大体上类似,但是在KMP的常数项上进行了进一步优化,使之更加完美。KMP自身已经很牛了,开淦!
KMP所解决的是一个包含问题
- 核心技术点在于一个next数组的创建和使用过程
- 先说next数组的创建吧
- next数组的0位置人为规定-1;
- next数组的1位置认为规定0;
- next数组所记录的是目标数组的当前索引前(不包括当前索引)的最长相等的前缀和后缀
- 举个例子
- 由上图得知索引位位x的最长前后缀的长度为4;
- 举个例子
- 先来个开胃菜,展示一下如何手算最长前后缀。(附带分析过程)
- 再说如何机算KMP的next数组
- 前提是一样的
- next[0]是-1
- next[1]是0
- 机算运用到了当前索引的前一个位置的next数组中的值;大家只用记住规则就好,到时候根据规则就可以记住代码!
- 如果next数组中当前索引的前一个的值所对应的字符 = 当前索引的前一个字符:str.charAt(next[index - 1]) == str.charAt(index-1);
- 等于的话
- next[index] = next[index-1]+1;
- 不等于的话:(这里存在一个向next数组左边推并对比的一个过程)
- 让str[next[index-1]] 与str[index-1]再进行对比,一直这样对比,如果相等了,next[当前] = next[当前推到的位置+1];
- 等于的话
- 这块需要在字符串数组和next数组这颠倒,若上面的理论不明白,可以直接配合代码食用
-
//判断要求的索引的坐标要求的next数组是否一样 private static int[] getNextArr(String s) { int[] next = new int[s.length()]; next[0] = -1; next[1] = 0; //开始计算next数组 a: for (int i = 2; i < s.length(); i++) { //记录住始终变得信息 int j = next[i - 1]; //如果不相等 b: while (j > 0 && s.charAt(i - 1) != s.charAt(j)) { //此时代表不相等,坐标后移 int index = next[j]; j=next[j]; if(s.charAt(j) == s.charAt(i-1)){//如果相等了 next[i] = index+1; continue a; } } //如果相等 if (s.charAt(i - 1) == s.charAt(next[i - 1])) { next[i] = next[i - 1] + 1; } } return next; }
-
- 如果next数组中当前索引的前一个的值所对应的字符 = 当前索引的前一个字符:str.charAt(next[index - 1]) == str.charAt(index-1);
- 前提是一样的
next数组出来后,就到了next数组的使用了
- 整体是一个记录有几个位置已经和目标数组一样了的关系。
- 如果当前字符串的索引和目标数组索引字符相等,相等数量+1,直到相等的数量完全相等,返回当前字符串数组index的位置-目标数组的长度+1;
- 如果当前字符串的位置和目标数组的位置的字符不相等,让目标数组的 next[当前位置的前一个的坐标]+1位置 与 字符串数组接着比较
- 同时这里有一个证明 str中的next[index-1]之前的位置证明不出目标字符串
接下来看全部代码
package LiKou.KMP;
import java.util.Arrays;
public class ZUOKMPTest {
public static void main(String[] args) {
//String s1 = "BBC ABCDAB ABCDABCDABDE";
String s1 = "BBC ABCDABAFABCDAB ABCDABCDABDE";
String s2 = "ABCDABAF";
int[] next = getNextArr(s2);
System.out.println(Arrays.toString(next));
int index = MyKMP(s1, s2, next);
System.out.println(index);
}
private static int MyKMP(String s1, String s2, int[] next) {
for (int i = 0, j = 0; i < s1.length(); i++) {
while (j > 0 && s1.charAt(i) != s2.charAt(j)) {
j = next[j - 1]; //在这是一个不断迭代的过程
}
if (s1.charAt(i) == s2.charAt(j)) {
j++;
}
if (j == s2.length()) {
return i - j + 1;
}
}
return -1;
}
//判断要求的索引的坐标要求的next数组是否一样
private static int[] getNextArr(String s) {
int[] next = new int[s.length()];
next[0] = -1;
next[1] = 0;
//开始计算next数组
a:
for (int i = 2; i < s.length(); i++) {
//记录住始终变得信息
int j = next[i - 1];
//如果不相等
b:
while (j > 0 && s.charAt(i - 1) != s.charAt(j)) {
//此时代表不相等,坐标后移
int index = next[j];
j=next[j];
if(s.charAt(j) == s.charAt(i-1)){//如果相等了
next[i] = index+1;
continue a;
}
}
//如果相等
if (s.charAt(i - 1) == s.charAt(next[i - 1])) {
next[i] = next[i - 1] + 1;
}
}
return next;
}
}
今天就到这,突然发现没有做kmp的深度认识,下个博客,聊聊马拉车manacher