KMP算法
本文分享自己对KMP算法的理解,尤其是最关键的next数组的求解。如果大家有任何问题或者我代码上有任何问题,请指出,相互交流。
1.最大匹配值介绍
一个字符串的最大匹配值:
前缀(前n-1个字符由1->n-1个构成的所有字符串) 和 后缀(后n-1个字符,由第2到倒数第一个字符构成的所有字符串)
能匹配到的相同的所有字符串中最大的长度
例如:ABABA
前缀 A AB ABA ABAB 后缀 A BA ABA BABA
则最大匹配值就是ABA的长度 3
2.next数组介绍
next数组的含义,就是每个字符对应自己前面小子串的最大匹配值
例如:ABABA
next数组索引: 0 1 2 3 4
子串: A AB ABA ABAB ABABA
前后缀相同的最大子串 - - A AB ABA
最大匹配值: 0 0 1 2 3
next[] 0 0 1 2 3
3.获取KMP的next数组-约定第一个数就是0
(不同于网上比较流行的next数组右移一位,约定为-1)
//获取KMP的next数组
private static int[] getNext(String source){
//构建next数组
int [] next = new int[source.length()];
next[0] = 0;//当只有一个字符时,最大匹配值就是0
//对字符串进行遍历
//i指向后缀 j指向前缀
for(int i = 1,j=0;i<source.length();i++){
//当对应的字符串不等时,应该先让j前移,去找到与i处字符对应相等的前缀中的索引,继续重新比较
//通过next数组本身,利用当前字符前面的最大匹配值进行回转。也就是next[j-1]。注意越界判断
while(j>0 && source.charAt(i)!= source.charAt(j)){
//此时我们移动j索引 移动位数= 已匹配的字符数j-对应的部分匹配值next[j-1]。即(j-next[j-1])
//j向前移动位数 j = j - (j-next[j-1]) = next[j-1]
j=next[j-1];
}
//当对应的字符相等时,j自增
if(source.charAt(i) == source.charAt(j)){
j++;
}
//相等后,让此时i对应的next值,赋值为j.然后i后移,进入下一轮循环
next[i] = j;
}
return next;
}
4.解决字符串匹配问题-暴力匹配原理复习
字符串匹配-暴力匹配
5.KMP算法解决字符串匹配问题-对暴力匹配的优化
主要思路都在注释中,请对比暴力匹配原理,耐心理解代码
其中最关键 j=next[j-1] 求解思路:
- 字符比较时,可以想象str1不动,比较不等时,索引不必全部回转,i不变,此时向右移动str2,已经比较过的字符不必再比较,找到重新开始比较的索引
- 根据next数组进行对 j 进行一定调整,此时我们移动j索引 移动位数= 已匹配的字符数j-前一个最大匹配值next[j-1]
- 搜索词向后移动位数就等于j向前移动位数,此时j的索引应该是当前索引-移动位数
- j = j - (j-next[j-1]) = next[j-1]
//KMP算法利用next数组
public static int kmpSearch(String str1,String str2,int [] next){
//原理同暴力匹配,但是不等时,不必全部回转,根据next数组进行对j进行一定调整,已经比较过的字符不必再比较,i不变
int i = 0;//遍历str1
int j = 0;//遍历str2
while(i<str1.length() && j<str2.length()){
//先后顺序不能反,应该先调整j的索引位置,再去比较。
//当两个字符遇到不等时,搜索词后移(j前移)。直到相等
while(j > 0 && str1.charAt(i)!=str2.charAt(j)){
//当遇到不等时,应该利用当前字符前面的最大匹配值进行回传。也就是next[j-1]
//此时我们移动j索引 移动位数= 已匹配的字符数j-对应的部分匹配值next[j-1]
//搜索词向后移动位数就等于j向前移动位数,此时j的索引应该是当前索引-移动位数
//j = j - (j-next[j-1]) = next[j-1]
j=next[j-1];
}
//当两个字符相等那就后移
if(str1.charAt(i)==str2.charAt(j)){
j++;
}
//i只是跟大循环相关,遍历大字符串,不受相等约束。
//当两个串有相同的字符时,那就i j 一起后移,如果遇到不等的,那只是回转j,i保持不变,等待str2移动继续比较
i++;
}
if(j==str2.length()){
return i-j;
}else{
//结束循环没返回就是没找到了
return -1;
}
}
6.主方法测试
public static void main(String[] args) {
// TODO Auto-generated method stub
String str1 = "BBC ABCDAB ABCDABCDABF";
String str2 = "ABCDABF";
//获取next数组
int [] next = getNext(str2);
System.out.println(Arrays.toString(next));
//调用kmp算法进行遍历查找
int index = kmpSearch(str1, str2, next);
if(index != -1){
System.out.println("匹配到的开始索引为"+index);
}else{
System.out.println("没有相关的匹配");
}
}