KMP算法的难点在于next数组和前缀表
KMP算法流程:
- 假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置
- 如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j] ),都令 i++,j++,继续匹配下一个字符;
- 如果 j != -1,且当前字符匹配失败(即 S[i] != P[j] ),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P相对于文本串 S 向右移动了 j - next [j] 位
换言之,将模式串 P 失配位置的 next 数组的值对应的模式串 P 的索引位置移动到失配处
首先,列出模式串 P 的所有子串:
然后,求得每一个子串的所有前缀与后缀。
前缀 指除了最后一个字符以外,一个字符串的全部头部组合;后缀 指除了第一个字符以外,一个字符串的全部尾部组合。
以子串abaab为例:
因此,它的前缀后缀的公共元素的最大长度为 2。
求得原模式串 P 的子串对应的各个前缀后缀的公共元素的 最大长度表 下图。
根据最大长度表 去求 next 数组:next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。
Java实现
private int[] getNext(String p) {
int M = p.length();
int[] next = new int[M];
next[0] = -1;
int j = 0;
int k = -1;
while (j < M - 1) {
if (k == -1 || p.charAt(k) == p.charAt(j)) {
next[++j] = ++k;
} else {
k = next[k];
}
}
return next;
}
int KmpSearch(String p, String t) {
// 根据模式字符串获得next数组
int[] next = getNext(p);
int N = t.length();
int M = p.length();
int i = 0;
int j = 0;
while (i < N && j < M) {
if (j == -1 || p.charAt(j) == t.charAt(i)) {
i++;
j++;
} else {
j = next[j];
}
}
if (j == M) {
return i - j;
} else {
return -1;
}
}