KMP算法原理:
当下标为5的字符不匹配时, 因为知道前五个字符的前缀A A 和后缀A A其实是一样的, 所以直接从模式串的第三位开始比较,不用回溯到开始的下一位,减少比较的次数。
怎么知道将模式串移动到哪个位置呢?就需要借助前缀后缀表(部分匹配表), 前缀表记录了字符串前后缀最大相等的值的长度
对于模式串 AABAAD
- A 没有前后缀 -> 0
- AA 前缀 A 后缀 A, 最大相等值A,长度为1 -> 1
- AAB 前缀 A, AA; 后缀B, AB,没有相等的值 -> 0
- AABA 前缀A, AA, AAB; 后缀A, BA, ABA 最大相等值A,长度为1 ->1
- AABAA 前缀A, AA, AAB, AABA; 后缀A, AA, BAA, ABAA, 最大相等值AA, 长度为2 ->2
- AABAAD 前缀A, AA, AAB, AABA, AABAA; 后缀D, AD, AAD, BAAD, ABAAD, 没有最大相等值 -> 0
由此得到部分匹配表
生成部分匹配表代码
private static int[] generatePrefix(String search) {
char[] searchChar = search.toCharArray();
int[] prefix = new int[(searchChar.length)];
//第一位字符,前后缀公共部分为0
prefix[0] = 0;
//i指向后缀末尾, j指向前缀末尾
for (int i = 1, j = 0; i < searchChar.length; i++) {
while (j > 0 && searchChar[i] != searchChar[j]) {
j = kmpTprefixable[j - 1];//
}
if (searchChar[i] == searchChar[j]) {
j++;
}
prefix[i] = j;
}
return prefix;
}
实现下图的模式串右移,意味着将指向模式串的指针从 “D” 移动到第三位 “B”(下标为2), 也就是上面匹配表中“D”的前一位 “A”对应的值
用java代码实现匹配
private static int KMPSearch(String source, String search, int[] prefix) {
for (int i = 0, j = 0; i < source.length(); i++) {
while (j > 0 && source.charAt(i) != search.charAt(j)) {
j = prefix[j - 1];//回溯到模式串前缀和后缀相等的位置
}
if (source.charAt(i) == search.charAt(j)) {
j++;
}
if (j == search.length()) {
return i - j + 1;
}
}
return -1;
}