概述
KMP算法往往是和朴素模式匹配算法相对比的,后者是一种暴力解决算法,用模式串与主串的每个子串一一比较。最终确定模式串在主串的起始位置。
主串:dfaabaafh3
模式串:aabaa
肉眼可以看出模式串是在主串第3个字符开始出现的。如果使用朴素模式匹配算法,则一共有(n-m)+1个子串,n是主串长度,m是模式串长度。所以说需要比较(n-m)+1次。每次又需要比较m次,所以最坏的情况下,整个朴素模式匹配的算法时间复杂度是o(mn)。
KMP算法的时间复杂度可以控制在 o(m+n),核心就是kmp算法可以让主串的指针不回溯,主串的指针一直向前移动,而动态的变化模式串的指针。而动态变化模式串指针的关键就是,当遇到某一个不匹配字符时,需要找到模式串当前这个字符之前的子串的最长相等前后缀。求出最长相等前后缀的长度,最终求出当前字符的下个指针应该是 (字串长度) - (字串长度 - (模式串匹配的长度)) + 1。求某个字符前面字符的公共前后缀的长度,定义两个指针,主串指针不断移动,如果与模式串不匹配,则模式串指针重置为1。
- 求next数组代码实现如下
//获取某个模式串的next数组(整个过程就是找模式串的不匹配字符之前的字串的最长相等前后缀,找到以后将前缀
// 移动到后缀位置,模式串指针此时等于(字串长度) - (字串长度 - 最长相等前后缀长度) + 1 )
private static int[] getNextArr(char[] chars) {
int[] next = new int[chars.length];//存储每个字符的next指针,也是从1开始存储
next[1] = 0;//如果第一次字符就不匹配了,则主串指针和模式串指针都需要++,但是需要先将模式串指针修改为0,再++
next[2] = 1;//如果遇到第二个字符不匹配了,则从第一个字符开始
//从第三个字符开始求字串的最长相等前后缀(注意,最长相等前后缀的长度不能等于子串的长度)
int i = 3;
while (i < chars.length) {
//定义两个指针,一个指向模式串,另一个指向模式串复制出来的主串
int modelIndex = 1;//从第一个字符开始扫描
int mainIndex = 2;//从第一个字符开始扫描
//主串的指针不能超过当前字串的长度(这里应该用主串的指针,因为模式串的指针是可能被重置为1的)
while (mainIndex < i) {
//模式串modelIndex与主串mainIndex指进行比较,如果相等,同时++
char mainChar = chars[mainIndex];
char modelChar = chars[modelIndex];
if (mainChar == modelChar) {
modelIndex++;
mainIndex++;
} else {
//只让mainIndex++
mainIndex++;
modelIndex = 1;
}
}
//循环结束后,modelIndex--,算出实际的相等前缀长度
modelIndex--;
int nextIndex = (i - 1) - ((i - 1) - modelIndex) + 1;
next[i] = nextIndex;
i++;
}
return next;
}
- 求模式串的位置实现代码如下
//获取模式串在主串中的位置,如果没有,则返回0(从下标1开始存元素的)
private static int indexOf(char[] mainChar, char[] modelChar, int[] next) {
//KMP算法的核心就是使得主串的指针不会回溯
int mainCharIndex = 1;//主串的指针
int modelCharIndex = 1;//模式串的指针
//while循环条件
while (mainCharIndex < mainChar.length ) {
char mainc = mainChar[mainCharIndex];
char modelc = modelChar[modelCharIndex];
if (mainc == modelc) {
//主串和模式串相等,继续比较
mainCharIndex++;
modelCharIndex++;
if (modelCharIndex >= modelChar.length) {
//说明模式串已经比较完了
break;
}
} else {
//如果不一致,则需要找到模式串当前不匹配字符的next下标
int nextIndex = next[modelCharIndex];
modelCharIndex = nextIndex;//让模式串从nextIndex开始与主串当前位置进行比较
if (modelCharIndex == 0) {
//如果模式串返回0,则表示第一个主串与模式串的第一个字符就不匹配,则此时需要同时++
modelCharIndex++;
mainCharIndex++;
}
}
}
if (modelCharIndex >= modelChar.length) {
//模式串指针已经全部走完了,可以匹配
return mainCharIndex - ( modelChar.length - 1) ; //主串指针减去模式串长度
} else {
return 0;
}
}
这里注意,当遇到不匹配字符时,要获取这个字符处的next指针,如果指针是0,则应该让主串指针和模式串指针同时++。
- 测试代码如下
public static void main(String[] args) {
char[] chars = {'