-
在BF算法中,正常的回退操作应该是 j 指针回退到1并且 i 指针也要回退一部分,但是这里不用,这就KMP算法的妙处,通过预处理算出模式串中不与s匹配的那个字符之前的字符串(不包括这个没有匹配上的字符)的最大公共前后缀长度,这个后缀就是s中不匹配的字符前面的一小部分字符串,前缀就是模式串的前面一小部分字符串,这样子再进行回退的时候,j 指针就只用回退到上面所说的那个前缀的最后一个字符,而 i 指针不用进行回退,接下来就继续 i 与 j + 1进行匹配,重复这样的操作,直到结束
-
在存储s和p时,从数组下标为1的地方开始存
-
Next[ i ]指的是以模式串下标为 i 的元素为末尾(算上这个元素),最长的公共前后缀的长度
-
匹配详解
- 首先初始化两个指针,指针 i 指向存储着s的数组的1索引,也就是s的第一个字符;另一个指针 j 指向p数组的0索引,也就是p的第零个字符
- 接下来就是 i 的循环
- 如果 j 指针没有指向0索引 并且 i 与 j + 1 不匹配,则将 j 指针回退到ne[ j ]的位置, 重新开始寻找
- 如果如果 i 与 j + 1 匹配,那么 j 向后移动一位,如果此时 j 移动到了模式串的最末尾,也就是在s中找到了相匹配的模式串,可以进行匹配后的逻辑操作,然后 j 指针回退到ne[ j ]的位置
- 此时到了一次循环的结尾,这个时候出现了两种情况:
- 第一种情况:i 与原来的 j + 1匹配成功了,并且 j 也向后移动了一位,为了继续向后匹配,需要 i++,进入下一个循环进行进一步的匹配
- 第二种情况:这个时候的 j 指向0索引了,并且 i 与 j + 1不匹配,也就是一开始第一个字符就不匹配了,这个时候就只能进入下一个循环,即 i++,重新开始匹配了
-
预处理next数组
- 首先初始化两个指针,指针 i 指向模式串的第二个元素,j 指向模式串的第零个元素
- 为什么不算第一个元素?因为在进行匹配时,如果模式串的第一个元素都匹配不上,此时 j 指针其实就指向模式串的第零个元素,退无可退了,因此ne[ 1 ] = 0,这个时候只能 i++ 重新开始匹配
- 接下来就是 i 的循环,每一次循环中都求出了以 i 指针指向的元素作为末尾(算上这个元素),最长的公共前后缀的长度
- 如果 j 指针没有指向0索引 并且 i 与 j + 1 不匹配,则将 j 指针回退到ne[ j ]的位置, 重新开始寻找
- 如果如果 i 与 j + 1 匹配,那么 j 向后移动一位
- 将 j 赋给next[ i ]
- 以 i 指针指向的元素作为末尾(算上这个元素),最长的公共前后缀的长度最长最长也就是i - 1因此考虑最极端的情况,只要匹配成功了一次,那么 j 就要向后移动一位的同时将自己赋给next[ i ]
- 首先初始化两个指针,指针 i 指向模式串的第二个元素,j 指向模式串的第零个元素
模板
- C++
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
//求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = ne[j];
// 匹配成功后的逻辑
}
}
- Java
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
//求模式串的Next数组:
for (int i = 2, j = 0; i <= m, i++) {
while (j != 0 && p[i] != p[j + 1]) {
j = ne[j];
}
if (p[i] == p[j + 1]) {
j++;
}
ne[i] = j;
}
//匹配
for (int i = 1; j = 0; i <= n; i++) {
while (j != 0 && s[i] != p[j + 1]) {
j = ne[j];
}
if (s[i] == p[j + 1]) {
j++;
}
if (j == m) {
j = ne[j];
//匹配成功后的逻辑
}
}
练习
01 KMP字符串
- 题目
- 题解
import java.io.*;
public class Main {
public static final int N = 100010;
public static final int M = 1000010;
public static char[] s = new char[M];
public static char[] p = new char[N];
public static int[] ne = new int[N];
public static int n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
n = Integer.parseInt(br.readLine());
p = (" " + br.readLine()).toCharArray();
m = Integer.parseInt(br.readLine());
s = (" " + br.readLine()).toCharArray();
for (int i = 2, j = 0; i <= n; i++) {
while (j != 0 && p[i] != p[j + 1]) {
j = ne[j];
}
if (p[i] == p[j + 1]) {
j++;
}
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i++) {
while (j != 0 && s[i] != p[j + 1]) {
j = ne[j];
}
if (s[i] == p[j + 1]) {
j++;
}
if (j == n) {
j = ne[j];
pw.print(i - n + " ");
}
}
pw.close();
br.close();
}
}