在讲述KMP算法前,首先要理解什么是部分匹配表。
1,部分匹配表
在这里插入图片描述
2,KMP算法
题目
思路分析
为什么可以进行上面的这种写法呢?是因为我们已经知道了最大前缀和后缀了, 那么我们下一回找的时候完全可以直接将str移动到最大后缀的地方,省下了不少的空间。
算法实现
package kmp.zuochenyun;
/**
* @version v1.0
* @ProjectName: 数据结构
* @ClassName: Code01_KMP
* @Description: 请描述该类的功能
* @Author: ming
* @Date: 2022/4/4 18:14
*/
public class Code01_KMP {
/**
* 创建一个部分匹配表
* @param ms
* @return
* 创建部分匹配表是通过动态规划的方式完成的
* 1,规定,部分匹配表next的0和1的位置始终为-1和0
* 2,之后的部分始终要用前面的部分匹配表来表示,例如
* 数组 ms[] = abbstabbecabbstabb?
* 现在我们要计算的是?的部分匹配表
* 1,设一个指针i,指向?
* 2,i-1的部分匹配表是8(8其实就是cn),这个时候可以分情况讨论
* 2.1,如果?等于ms[8],就说明?的部分匹配表的值为next[i-1] + 1
* 2.2,如果i-1的部分匹配表大于0且不符合2.1的情况,就说明我可以往前跳,跳到next[cn]的位置
* 也就是e的位置,之后继续进行步骤2的判断讨论,e的next值是3,假设?是s,那么i的next就是4
* 2.3,如果不符合2.1和2.2的情况,同时cn是不大于0的情况,也就是我们无法往前跳了,即前面没有
* 一个和我相同的字符,这个时候i的部分匹配值就是0。
*/
public static int[] getNextArray(char[] ms) {
if (ms.length == 1) {
return new int[] {-1};
}
int[] next = new int[ms.length];
next[0] = -1;
next[1] = 0;
int i = 2;
//cn表式指向和i-1比较的位置,也是当前位置的部分匹配表
int cn = 0;
while (i < next.length) {
if (ms[i - 1] == ms [cn]) {
/**
*下面的代码等同于
* cn++;
* next[i] = cn;
* i++;
*/
next[i++] = ++cn;
} else if (cn > 0) {
cn = next[cn];
} else {
next[i++] = 0;
}
}
return next;
}
/**
* @param s
* @param m
* @return
*/
public static int getIndexOf(String s, String m) {
if (s == null || m == null || m.length() < 1 || s.length() < m.length()) {
return -1;
}
char[] str1 = s.toCharArray();
char[] str2 = m.toCharArray();
//是str1的指针
int i1 = 0;
//是str2的指针
int i2 = 0;
//获取部分匹配表
int[] next = getNextArray(str2);
while (i1 < str1.length && i2 < str2.length) {
//如果相等就让指针都向后移动一位
if (str1[i1] == str1[i2]) {
i1++;
i2++;
//如果next[i2] == -1,也就是在第一个位置就不一样,那么就让i1指针向后移动一位
} else if (next[i2] == -1){
i1++;
//否则就要让i跳到next[i2]上,进行一个加速。
} else {
i2 = next[i2];
}
}
//如果i2越界,那么就说明我们已经找到了,他在s1的初始位置是i1-i2,如果i1越界,就说明不包含。
return i2 == str2.length ? i1 - i2 : -1;
}
public static void main(String[] args) {
String str = "abcabcababaccc";
String match = "ababa";
System.out.println(getIndexOf(str, match));
}
}