KMP算法,字符串模式匹配,返回匹配的索引位置
KMP算法,又称作“看猫片”算法(误),是一种改进的字符串模式匹配算法,可以在O(n+m)的时间复杂度以内完成字符串的匹配操作,其核心思想在于:当一趟匹配过程中出现字符不匹配时,不需要回溯主串的指针,而是利用已经得到的“部分匹配”,将模式串尽可能多地向右“滑动”一段距离,然后继续比较。
- 朴素的字符串模式匹配算法
求一个字符串(模式串)在另一个字符串(主串)中的位置,称为字符串模式匹配。 暴力求解法。
在朴素的字符串模式匹配算法中,我们对主串S和模式串T分别设置指针i和j,假设字符串下标从0开始,初始时i和j分别指向每个串的第0个位置。在第n趟匹配开始时,i指向主串S中的第n-1个位置,j指向模式串T的第0个位置,然后逐个向后比较。若T中的每一个字符都与S中的字符相等,则称匹配成功,否则,当遇到某个字符不相等时,i重新指向S的第n个位置,j重新指向T的第0个位置,继续进行第n+1趟匹配。
- 改进的字符串模式匹配算法——KMP算法
在上面的例子中,我们可以看到,当i=8,j=5时,S[8]与T[5]不相等,于是置i=4,j=0,相当于将模式串向右移动一位,再开始下一趟匹配。然而,通过观察我们可以发现,之后的两趟匹配,即i=4,j=0以及i=5,j=0都是不必要的。这是因为,在之前的一趟匹配过程中,我们已经部分匹配了T的子串“abaab”。此时将T向右移动一位,则相当于对T中的“abaab……”与S中的“baab……”进行匹配,显然无法匹配成功。继续右移T,则相当于对T中的“abaab……”与S中的“aab……”进行匹配,依然无法匹配成功。只有当T向右移动3位后,此时对T中的“abaab……”与S中的“ab……”进行匹配,才会有成功的可能,也就有必要向后继续进行比较。
对于模式串T,next[j]代表了T的前j个字符组成的子串中,其前缀和后缀的最长公共串的长度。
求解字符串T的next数组的算法如下:
1、next[0]=-1, next[1]=0。
2、在求解next[j]时,令k=next[j-1],
3、比较T[j-1]与T[k]的值,
a. 若T[j-1]等于T[k],则next[j]=k+1。
b. 若T[j-1]不等于T[k],令k=next[k],若k等于-1,则next[j]=0,否则跳至3。
下面给出KMP算法的Java代码。整个算法分为两部分,一是next数组的求解,二是KMP匹配过程。
public class KMP {
/**
* 求出一个字符数组的next数组
* @param t 字符数组
* @return next数组
*/
public static int[] getNextArray(char[] t) {
int[] next = new int[t.length];
next[0] = -1;
next[1] = 0;
int k;
for (int j = 2; j < t.length; j++) {
k=next[j-1];
while (k!=-1) {
if (t[j - 1] == t[k]) {
next[j] = k + 1;
break;
}
else {
k = next[k];
}
next[j] = 0; //当k==-1而跳出循环时,next[j] = 0,否则next[j]会在break之前被赋值
}
}
return next;
}
/**
* 对主串s和模式串t进行KMP模式匹配
* @param s 主串
* @param t 模式串
* @return 若匹配成功,返回t在s中的位置(第一个相同字符对应的位置),若匹配失败,返回-1
*/
public static int kmpMatch(String s, String t){
char[] s_arr = s.toCharArray();
char[] t_arr = t.toCharArray();
int[] next = getNextArray(t_arr);
int i = 0, j = 0;
while (i<s_arr.length && j<t_arr.length){
if(j == -1 || s_arr[i]==t_arr[j]){
i++;
j++;
}
else
j = next[j];
}
if(j == t_arr.length)
return i-j;
else
return -1;
}
public static void main(String[] args) {
System.out.println(kmpMatch("abcabaabaabcacb", "abaabcac"));
}
}