朴素的模式匹配算法,是将子串与主串匹配,如果发生不匹配,则子串下标退回到原点,主串下标加1,从头开始匹配。这样的算法简单易懂,但进行了很多次无用的匹配,时间复杂度为O(m*n)。
而KMP模式匹配在朴素的模式匹配上加以改进,省略掉了很多的重复的比较过程,例如主串为abbabbe,子串为abbe
kmp模式匹配就是这样通过检查子串中的后缀是否与前缀相匹配的方法,确定子串回溯的位置,这样就能省去很多次无用的匹配,优化了算法。
于是问题的关键变成了怎样找到子串应该回溯的位置。子串回溯的位置只有子串的结构有关,子串的每一个位置都有一个回溯值,当子串与主串再此发生不匹配时,子串就回溯到该位置。定义该回溯数组为next【】。
ok,i think you must want to say shut your mouth and show me the code.
// 该方法用来寻找子串的next[]数组
public static int [] getnext(String T)
{
// j表示子串中的下标
int j = 1;
// k表示前缀与子串后缀相匹配的最大值
int k = 0;
int [] next = new int[T.length()];
// 初始化next[0]=-1,next[1]=0
next[0] = -1;
next[1] = 0;
while(j<T.length()-1)
{
// 如果前缀等于后缀,next[j+1]=k+1
if(T.charAt(j)==T.charAt(k))
{
next[j+1] = k+1;
j++;
k++;
}
else if (k == 0)
{
next[j+1] = 0;
j++;
}
// 如果不匹配,k值回溯。相当于子串中进行一次模式匹配
else
k = next[k];
}
// 此处用来验证next[]数组是否正确
// for (int z = 0; z < next.length; z++) {
// System.out.print(next[z]);
// }
return next;
}
在得到next[]数组后,即可进行kmp模式匹配
// 实现kmp模式匹配
public static int kmp_index(String s,String t) {
int i = 0;
int j = 0;
// 获得next[]数组
int next[] = getnext(t);
while (i<s.length()&&j<t.length()) {
if (j == -1||s.charAt(i) == t.charAt(j)) {
i++;
j++;
}
// 如果不匹配,j回溯至当前j值的next[j]处
else {
j = next[j];
}
}
if (j == t.length()) {
return i-t.length();
}
else {
return -1;
}
}
// 测试kmp模式匹配
// public static void main (String [] args) {
// String string1 = "aaabbbaaab";
// String string2 = "bbbaaa";
// getnext(string2);
// int i = kmp_index(string1, string2);
// System.out.println(i);
// }
}