1、什么是K.M.P算法
K.M.P算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。
总得来说,它是一种字符串匹配算法,相比于暴力破解,有了一定的改进。
首先我们先看下对于字符串匹配的暴力破解方式:
/**
* 暴力破解方法
* @param str
* @param mstr
* @return
*/
public static int strMatch(String str, String mstr) {
char[] s = str.toCharArray();
char[] m = mstr.toCharArray();
int i = 0; // 主串的位置
int j = 0; // 匹配串的位置
while (i < s.length && j < m.length) {
if (s[i] == m[j]) { // 当两个字符相同,就比较下一个
i++;
j++;
} else {
i = i - j + 1; // 一旦不匹配,i后退,回退到字符串匹配循环开始位置后一位
j = 0; // j归0
}
}
// 判断是否有匹配串,返回第一个发现的匹配串位置
if (j == m.length) {
return i - j;
} else {
return -1;
}
}
暴力破解的思路就是,匹配串从主串开始位置进行匹配,如果有一个字符串不匹配,则主串从开始匹配位置往后移动1位,匹配返回开始位置重新与主串进行比较,循环往复。
2、K.M.P算法相比于暴力破解改进了什么?
我们观察暴力破解算法,很明显会注意到,如果匹配串与主串不匹配了,那么主串指针会退到字符串匹配循环开始位置后一位,匹配串指针也是回溯到开始位置,并与主串指针所指向位置重新进行匹配,效率很低,但如果移动多位数据又会不准确,那么主串指针及匹配串指针要移动到什么位置是一个很重要的改进点,而K.M.P给了我们一个思路:
利用匹配失败后的信息,主串指针指向位置不变,改变模式串指针指向位置进行匹配,以尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息 。
观察匹配串中从前往后遍历,分解出的子串有没有与开始字符串相匹配,匹配了几位,用数据展示出来就形成了一个前缀表:
i str next[i]
0 A 0
1 AB 0
2 ABA 1
3 ABAB 2
4 ABABC 0
5 ABABCA 1
6 ABABCAB 2
7 ABABCABA 3
8 ABABCABAA 1
代码展示如下:
private int[] getNext(String pattern) {
int[] next = new int[pattern.length()];
char[] charP = pattern.toCharArray();
int len = 0, k = 1;
next[0] = 0;
while(k < charP.length){
if(charP[len] == charP[k]){
len++;
k++;
next[k] = len;
}else{
if(len > 0){
/** 如果不相等,说明这次多出来的这个字符并没有让共有字符串的长度加1,而且,可能还会直接影响到上一次的共有字符串长度
这里的做法是:因为多出来一个字符,而且charArr[i] != charArr[len],那这次已经不能拿上一次共有字符串位置上的字符来做比较了,必须拿上上一次的结果*/
len = next[len - 1];
}else{
next[k] = len;
k++;
}
}
}
for(int i = charP.length - 1; i > 0; i--) next[i] = next[i - 1];
next[0] = -1;
return next;
}
这样,当字符串不匹配时,匹配串可以根据前缀表移动固定的位置,而主串指针不动。代码展示如下:
public int strStr(String text, String pattern) {
if(text == null || pattern == null) return -1;
if(pattern.length() == 0) return 0;
int[] next = getNext(pattern);
char[] charT = text.toCharArray();
char[] charP = pattern.toCharArray();
int i = 0, j = 0;
while (i < charT.length && j < charP.length){
if(charT[i] != charP[j]){
j = next[j];
if(i == -1){
i++;
j++;
}
}else{
i++;
j++;
}
}
if(j == charP.length) return i - j;
return -1;
}