KMP算法(Knuth-Morris-Pratt算法)是由Donald Knuth、James H. Morris Jr. 和Vaughan Pratt三人共同提出的,因此得名。这是一种改进的字符串匹配算法,旨在高效地解决在一个文本串(主串)中查找一个模式串(子串)出现的位置的问题。与朴素的暴力匹配算法相比,KMP算法的关键优势在于它能够利用已经得到的匹配信息,避免在发生不匹配时重新检查文本串中已经比较过的字符,从而减少了不必要的比较,提高了搜索效率。
算法核心:
KMP算法的核心在于构建一个“部分匹配表”(也称为“失配表”或“前缀后缀表”),通常称为next
数组。next
数组记录了模式串中每个位置前面的子串中,有多大长度的真前缀等于其对应的真后缀。这里的真前缀是指不包括整个子串本身的前缀,真后缀同理。next
数组的构建基于这样一个事实:如果模式串中的某个前缀等于某个后缀,那么当这个前缀与文本串中的相应部分匹配失败时,模式串可以直接跳过这两个相等的部分继续比较,而无需回溯文本串的指针。
KMP算法步骤:
-
预处理:首先根据模式串构建
next
数组。next[j]
表示当模式串的前j个字符与文本串匹配失败时,模式串应该跳转到哪个位置继续比较以避免重复比较。 -
匹配过程:然后开始在文本串中进行匹配。初始时,主串和模式串的指针都指向各自的起始位置。如果当前字符匹配成功,则两个指针都向前移动;如果不匹配,模式串的指针根据
next
数组的值来决定跳转的位置,而主串的指针保持不变。 -
终止条件:当模式串的最后一个字符与文本串中的某个字符匹配成功时,说明找到了一个匹配的位置。此时可以返回该位置作为匹配成功的起始索引。如果模式串的指针移动到其末尾,则匹配完成;否则,继续进行比较。
KMP算法的时间复杂度为O(n + m),其中n是文本串的长度,m是模式串的长度,这使得它在处理长文本和模式串时尤其高效。
完整代码:
//串的匹配模式算法--KMP
public class KMP {
// kmp函数
// S 主串
// T 模式串
// next next数组
public static int kmp(String S, String T, int[] next) {
int i = 0, j = 0;
while (i < S.length() && j < T.length()) {
if (j == -1 || S.charAt(i) == T.charAt(j)) {
++i;
++j;
} else {
j = next[j];
}
}
if (j >= T.length()) {
return i - T.length();
} else
return 0;
}
// 获取next数组函数
// T 模式串
// next next数组
public static void get_next(String T, int[] next) {
int i = 2, j = 0;
next[0] = -1;
next[1] = 0;
while (i < T.length()) {
if (j == -1 || T.charAt(i - 1) == T.charAt(j)) {
next[i] = j + 1;
++j;
++i;
} else {
j = next[j];
}
}
}
public static void main(String[] args) {
String t = "abcac";// 模式串
String s = "ababcabcacbab";// 主串
int[] next = new int[t.length()];
get_next(t, next);// 获取next数组
System.out.println(kmp(s, t, next));
}
}