一、算法原理
一般匹配字符串时,我们从目标字符串str(假设长度为n)的第一个下标选取和ptr长度(长度为m)一样的子字符串进行比较,如果一样,就返回开始处的下标值,不一样,选取str下一个下标,同样选取长度为n的字符串进行比较,直到str的末尾(实际比较时,下标移动到n-m)。这样的时间复杂度是O(n*m)。
KMP匹配的核心思想是利用之前判断过信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置,省去了大量的计算时间。
注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。
对于目标字符串ptr,ababaca,长度是7,所以next[0],next[1],next[2],next[3],next[4],next[5],next[6]分别计算的是
a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀的长度。由于a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀是“”,“”,“a”,“ab”,“aba”,“”,“a”,所以next数组的值是[0,0,1,2,3,0,1]。
next数组就是说一旦在某处不匹配时,移动ptr字符串,使str的对应的最大后缀和ptr对应的最大前缀对齐,然后继续向后比较。
next数组的值,就是下次往前移动字符串ptr的移动距离。比如next中某个字符对应的值是4,则在该字符后的下一个字符不匹配时,可以直接移动往前移动ptr 4个长度,再次进行比较判别。
二、Java实现
1.获取next数组
public int[] getNext(String b)
{
int len=b.length();
int j=0;
int next[]=new int[len+1];//next表示长度为i的字符串前缀和后缀的最长公共部分,从1开始
next[0]=next[1]=0;
for(int i=1;i<len;i++)//i表示字符串的下标,从0开始
{//j在每次循环开始都表示next[i]的值,同时也表示需要比较的下一个位置
while(j>0&&b.charAt(i)!=b.charAt(j))
j=next[j];
if(b.charAt(i)==b.charAt(j))j++;
next[i+1]=j;
}
return next;
}
2.KMP算法
public void search(String original, String find, int next[]) {
int j = 0;
for (int i = 0; i < original.length(); i++) {
while (j > 0 && original.charAt(i) != find.charAt(j))
j = next[j];
if (original.charAt(i) == find.charAt(j))
j++;
if (j == find.length()) {
System.out.println("find at position " + (i - j));
System.out.println(original.subSequence(i - j + 1, i + 1));
j = next[j];
}
}
}