KMP算法【字符串搜索算法】
KMP算法
1. 算法核心
- 利用匹配失败后的信息
- 尽量减少模式串(B)与主串(A)的匹配次数
- 以达到快速匹配的目的
- 通过一个 next 数组,保存模式串(B)中 前后最长公共子序列的长度,每次回溯时,通过 next 数组找到,前面匹配过的位置,省去了大量的计算时间
2. 如何减少匹配次数
2.1. 字符串的前缀和后缀
比如字符串:ababacb
前缀集合 | 后缀集合 |
---|---|
\(\left \{ a,ab,aba,abab,ababa,ababac \right \}\) | \(\left \{ b,cb,acb,bacb,abacb,babacb \right \}\) |
2.2. 构建next数组
注意:当只有一个元素,它没有最长公共前后缀【next[0] = 0】
3. 代码实现
package com.kmp;
public class KMP {
static List<Integer> list = new ArrayList<>();
public static void main(String[] args) {
String s = "BBC ABCDAB ABCDABCDABDEdsafeABCDABD";
String target = "ABCDABD";
int[] next = kmpNext(target);
kmpSearch(s, target, next);
System.out.println(list);
}
public static List<Integer> kmpSearch(String s, String target, int[] next){ // 2个
int m = str2.length();
// 遍历
for (int i = 0, j = 0; i < str1.length(); i++) {
while (j > 0 && s.charAt(i) != target.charAt(j)){ // 如果不等了,就一直向前推进,找到上一个相等的位置
j = next[j - 1];
}
if (s.charAt(i) == target.charAt(j)){
j++;
}
if (j == m){
list.add(i - m + 1);
j = 0; // j 重置【匹配上了之后,j 重头开始匹配】
}
}
return list;
}
// 获得一个模式串的 next 数组
public static int[] kmpNext(String target) { // 单个
int[] next = new int[target.length()];
next[0] = 0;
for (int i = 1, j = 0; i < target.length(); i++) {
while (j > 0 && target.charAt(i) != target.charAt(j)){
j = next[j - 1];
}
if (target.charAt(i) == target.charAt(j)) {
next[i] = ++j;
}
}
return next;
}
}