KMP算法
KMP算法就是利用一个next[]数组让子串str2没必要每次匹配失败之后都要去从头开始又和str1的下一个位置匹配,这样一种加速策略能够加速字符串的匹配。而next[]数组中,next[i]表示的是该位置之前的字符串中,前缀子串与后缀子串相同的最大长度。就比如下图,i位置的前缀子串与后缀子串相同的最大长度为2。也就是对于i来说,前缀从0开始往后走,后缀从i-1开始往前走,直到不相遇或者前缀后缀重合为止。

而既然i位置前面这段后缀与从头开始的前缀相等,只是到了i位置之后才匹配不到,而i位置之前的匹配都已经通过了,说明匹配的母串str1中当前位置之前的那一段与i位置前面的这段是相等的,那么接下来的匹配就没有必要再回到开始去了,只需要去与i位置之前后缀相等的前缀的后一个位置j开始匹配就可以了。如下图所示

next数组正是利用了这种思想来生成的,具体的next[]数组生成过程如下图所示:

有了next[]数组之后,接下来就开始进行子串匹配。匹配过程如下:



代码如下:
public class KMP {
public static int getStartIndex(String str1, String str2) {
//str1为空串的话,str2必然不为str1的子串
if (str1.length() == 0) {
return -1;
}
//str2为空串的话,其在str1中出现的位置必然为首位
if (str2.length() == 0) {
return 0;
}
//获取next[]数组
int next[] = getNextArray(str2);
int i = 0, j = 0;
while (i < str1.length() && j < str2.length()) {
//在该位置匹配成功,两个字符串都继续往下走
if (str1.charAt(i) == str2.charAt(j)) {
i++;
j++;
} else if (j == 0) { //str2都调回到了初始位置还是没法和str1该位置匹配,说明i位置是真不行,str1需要往前走
i++;
} else { //j往前跳,跳回到其最长相等前后缀中的前缀的后一位置
j = next[j];
}
}
//如果j走完s2了,说明匹配成功,子串初始位置是i-j,而j没走完的话则说明匹配不成功
return j == str2.length() ? i - j : -1;
}
public static int[] getNextArray(String s) {
if (s.length() == 1) {
return new int[]{-1};
}
int[] next = new int[s.length()];
//0号位置之前并没有前缀与后缀,设为-1
next[0] = -1;
//1号位置之前只有一个元素,即前缀与前缀相等,设为0
next[1] = 0;
int i = 2, k = 0;
while (i < s.length()) {
//i-1位置与要比较的位置的元素相等,说明该位置前面相等的前缀与后缀又增长了1,写入该位置
if (s.charAt(i - 1) == s.charAt(k)) {
next[i++] = ++k;
} else if (k == 0) { //已经比到头了,没有相等的前缀后缀
next[i++] = 0;
} else { //往前跳去比
k = next[k];
}
}
return next;
}
public static void main(String[] args) {
String str1 = "aaabacbaaabaaabacaaaabac";
String str2 = "aaabacaaa";
System.out.println(getStartIndex(str1, str2));
}
}

4023

被折叠的 条评论
为什么被折叠?



