kmp模式匹配

  1. kmp 匹配的目的是 询问一个字符串s1 len(s1)<=len(s2) 是否是 字符串s2 的子串
  2. s1 叫做 pattern模式串
  3. 暴力匹配都知道
  4. abcde 前缀:a ab abc abcd abcde 真前缀不包括abced 后缀同理
  5. 所以我们求出s1prefix[]要后移一位,才是最长公共(真)前后缀

为什么prefix[]右移???

当不断比较s2[i]prefix[j] ,相同就比较下一个,不同
就把pattern串的上一个字符pattern[j-1](公共前后缀最大)的第一个后缀字符拿来与当前s2[i]比较,所以prefix[j]右移一位,j位保存的是j-1位的最长前后缀的 (第一个) 后缀下标

  • 先求出prefix[]前缀表,最长公共的(真)前后缀
    例如
aa 1
aba 1
abc 0
abcda 1

怎么求prefix[]

  • 不断的拿ilen相比较,相同则 prefix[j]=++len;
  • 不同 就 找len = prefix[len - 1]前一个字符的前子缀的最后一个字符与pattern[i]相比较,一直循环到相同即可,

prefix[]求出后,开始拿模式串进行匹配

  • 遇到不同则 比较pattern串的上一个字符的后缀字符,
  • 如果模式串上一个字符的后缀字符是-1(此时模式串在第一个字符pattern[0]),仍然将-1移到s2[i]这点来,但是不比较,那么数组模拟就是同时i++ j++去比较下一个,(实际效果与移动的效果是一样的)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 字符串最长公共前后缀
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



void prefix_table(char pattern[],int prefix[],int n) {
prefix[0] = 0; // 第一个的公共前后缀为0
int len = 0;
int i = 1;
while ( i < n ) {
if ( pattern[i] == pattern[len] ) {
len++;
prefix[i] = len;
i++;
}
else {
if ( len > 0 ) { // 避免在 len = 0 的时候指向不明内存区域
len = prefix[len - 1]; // i 此轮不变,while () 进入下一路寻找 公共子缀合
}
else { // len = 0
prefix[i] = len;
i++;
}
}
}
}
// 第一个改成-1 暂时还不知道为什么
void move_prefixtable(int prefix[],int n) {
// 抹掉了最后一个prefix,

for (int i = n-1; i > 0; i--) {
prefix[i] = prefix[i-1];
}
prefix[0] = -1;
}
void kmp_serach(char text[] ,char pattern[]) { // text是原字符串,pattern是 需要查询的字符串 , 查询他是不是为text的子串
int n = strlen(pattern);
int* prefix = malloc( sizeof(int) * n); // malloc 在堆中分配内存别忘了 用指针
prefix_table(pattern,prefix,n);
move_prefixtable(prefix,n);

int m = strlen(text);
int i = 0, j = 0;
while (i<m) { // i 是text上的指针
if ( j == n-1 && pattern[j] == text[i] ) {
printf("Found at %d",i-j); // i - j 的含义: i , j 此时是相同的,因为我们移动的是字符串,所以i-j相当于 把 他们重新移回去
j = prefix[j];//继续找下一个 子串开头的位置
}
if ( pattern[j] == text[i] ) {
i++; j++;
}
else {
j = prefix[j];
if ( j == -1 ) { // 说明 需要 将 pettern 向右移动 对准现在的i
i++; j++;//跳过这个pattern[0] != text[i]不相等的 , prefix[0] = -1;
}
}
}
}
int main() {
// char pattern[] = "ABABCABAA";
// char text[] = "ABABABCABAACCCCCA";
//好久没有写c,数组大小没有声明
//char pattern[];
//char text[];
char pattern[1e6];
char text[1e6];
scanf("%s%s",text,pattern);
kmp_serach(text,pattern);
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!