KMP算法
字符串匹配算法
时间复杂度:O(n)
难点:生成next[]数组,还有理解为什么这样匹配是正确的
生成next数组:自己动手举个例子会好理解
为什么这样匹配是正确且避免回溯:若str1和str2已经匹配了一段,则str2是返回其最大前缀的后一个字符的位置(即next[x]),此时str1前面的字符和str2的最大前缀还是匹配上的,这就避免了回溯了。
详细讲解可参考blog:https://blog.csdn.net/v_july_v/article/details/7041827
(这个blog可以看懂为什么要这样匹配,算法的写法,但是next数组的生成还是要自己举例子才更直观)
/**
* KMP 求str1中匹配到的str2的起始位置
* O(n)
*/
public class KMP {
public static void main(String[] args){
String str1 = "abcd1234546edgeabc";
String str2 = "12345";
System.out.println(new KMP().getIndexOf(str1,str2));
}
/**
* KMP主函数
* @param str1 需要搜素字符串
* @param str2 被匹配的字符串
* @return
*/
public int getIndexOf(String str1,String str2){
if(str1 == null || str2 == null || str2.length() < 1 || str1.length() < str2.length()){
return -1;
}
int index1 = 0;
int index2 = 0;
int[] next = getNextArr(str2);
while(index1 < str1.length() && index2 < str2.length()){
// 两个字符匹配,那么就一起继续往下匹配
if(str1.charAt(index1) == str2.charAt(index2)){
index1++;
index2++;
// 如果此时两个字符不匹配,且此时index2=0,那就让str1的下一个字符继续与str2匹配
}else if(next[index2] == -1){
index1++;
// 如果两个字符不匹配,且index2也不是等于0(即str1和str2匹配了一段),此时要把index2挪动到它的最大前缀的后一个字符,
// 再继续跟index1所指向的字符匹配
}else{
index2 = next[index2];
}
}
return index2 == str2.length() ? index1 - index2 : -1;
}
/**
* 求next数组
* next数组:str对应的index位置的0-index-1的最大匹配前后缀的长度,例如abcd1234cdabcd的最大匹配前后缀是4(abcd)
* @param str
* @return
*/
public int[] getNextArr(String str){
if(str.length() == 1){
return new int[]{-1};
}
int[] next = new int[str.length()];
// 人为规定next[0] = -1;next[1] = 0(因为其没有前缀嘛,然后前后缀又不能是同一个字符)
next[0] = -1;
next[1] = 0;
// 跳到的地方的index(这个很难解释,看视频吧或者找KMPblog吧)
int cn = 0;
int index = 2;
while(index < next.length){
//如果相等,那说明求出来了
if(str.charAt(cn) == str.charAt(index-1)){
next[index++] = ++cn;
// 否则让它将继续往前跳
}else if(cn > 0){
cn = next[cn];
}else {
// 如果cn < 0,说明cn已经跳到0的位置了,index位置的最大前后缀为0
next[index++] = 0;
}
}
return next;
}
}