KMP算法的核心就是求解模式串的next数组,next[K]表示前K-1个字符构成的字符串的最长公共前后缀。当在K位置匹配失败时,想象把模式串滑动至其最长前缀与后缀吻合,继续比较匹配串的当前位置和模式串的最长前缀后的第一个字符,所以next数组记录的最长公共前后缀实际上表示在当前位置匹配失败时下一个轮到谁来匹配。特别是在模式串的第一个字符匹配失败时,模式串向后滑动一位。
我们注意到next数组只是记录最长公共前后缀,而忽略了一种特殊情况:就是最长前缀的后一个字符和模式串当前位置的字符相同,也即:
Pattern[K] = Pattern[next[K]]
而当前位置匹配失败了,所以用next[K]来匹配也一定会失败。so,这里可以小小的优化一下,在求出next数组后,对next数组进行一次遍历,对每个位置进行一次判断,如果当前位置K的字符和位置next[K]的字符相同,就一直向前找,直到next[T]对应的字符不同为止。
talk is cheap,show me the code!
package blog.xu;
public class Main {
private static int count = 0; //用来记录匹配期间的比较次数
public static int[] getNext(String pattern){ //传统next数组生成方式
int[] next = new int[pattern.length()];
next[0]=-1;
for(int i = 1 ; i < pattern.length() ; ++i){
int temp = next[i-1];
while(temp != -1 && pattern.charAt(temp) != pattern.charAt(i-1)) temp = next[temp];
next[i] = ++temp;
}
return next;
}
public static int[] getNextImprove(String pattern){ //改进后的next数组生成
int[] next = new int[pattern.length()];
next[0]=-1;
for(int i = 1 ; i < pattern.length() ; ++i){
int temp = next[i-1];
while(temp != -1 && pattern.charAt(temp) != pattern.charAt(i-1)) temp = next[temp];
next[i] = ++temp;
}
for(int i = 1 ; i < next.length ; ++i){ //上述做优化的一次next数组遍历
int temp = i ;
while(next[temp] != -1 && pattern.charAt(i) == pattern.charAt(next[temp])){
temp = next[temp];
}
next[i] = next[temp];
}
return next;
}
public static int KMP(String pattern,String str,boolean imp){ //返回匹配成功的次数
int[] next;
if(imp) next = getNextImprove(pattern);
else next = getNext(pattern);
int p_index = 0 ,num = 0;
for(int i = 0 ; i < str.length() ; ++i){
while(p_index != -1 && pattern.charAt(p_index) != str.charAt(i)){
p_index = next[p_index];
count++;
}
if(p_index == pattern.length()-1){
num++;
p_index = next[p_index]; //这里假设最后一位匹配失败,用来继续向后匹配
i--; //下一次匹配还是从这里开始,所以用i--来阻止匹配串的指针向后移动
}
else{
p_index++;
count++;
}
}
return num;
}
public static void main(String[] args) {
System.out.println(KMP("aaab","aaacaaabaaacaaab",false));
System.out.println(count);
count = 0 ;
System.out.println(KMP("aaab","aaacaaabaaacaaab",true));
System.out.println(count);
}
}