一、KMP算法
不重复描述,具体内容请自行查找。
二、代码
public int strStr(String haystack, String needle) {
int length_h=haystack.length();
int length_n=needle.length();
int[] pi=new int[length_n];//创建一个与needle等长的数组
//用于存放字符串的π函数值
if(length_h<length_n){
//主串比子串小
return -1;
}
else if(length_n==0){
// 子串长为0
return 0;
}
//i为新字符下角标,j为π函数值,先对needle进行遍历
for(int i=1,j=0;i<length_n;i++){
//当j>0,新增字符又无法与原字符串匹配
while (j>0 && needle.charAt(j)!=needle.charAt(i)){
j = pi[j-1];//不断向真前缀的真前缀回溯
//知道j=0||新字符与真前缀可以匹配
}
//如果新字符与真前缀新字符相同表明匹配,函数值+1
if(needle.charAt(j)==needle.charAt(i)){
j++;
}
pi[i]=j;//将长度为i时的函数值存入
}
//同上,i从0开始匹配的原因是实际匹配字符为needle中的字符
for (int i=0,j=0;i<length_h;i++){
while (j>0 && haystack.charAt(i) != needle.charAt(j)){
j = pi[j-1];
}
if(haystack.charAt(i)==needle.charAt(j)){
j++;
}
if(j==length_n){
return i-j+1;//i为当前匹配字符下角标,j为匹配字符串长度,i-j即为匹配前元素下角标
//i-j+1才是第一个匹配字符的下角标
}
else if(length_n-j>length_h-i-1){
//length_h-i-1为主串剩余字符数
//length_n-j为子串剩余字符数
//当子串剩余字符数比主串剩余字符数多,即为不可能,可以提前结束
return -1;
}
}
return -1;
}
三、思路解析
大部分思路解析已经在注释中完成。
最简单的实现方法是暴力遍历,暴力遍历唯一的问题就是花费大量的时间重复遍历,导致时间复杂度大大增加,尽管这种情况出现的次数应该很少。
KMP算法的最大作用是通过函数值记录,来给出了一个回溯的最大值,通过不断向真前缀回溯,保证了尽可能少的重复遍历。