- kmp 匹配的目的是 询问一个字符串
s1
len(s1)<=len(s2)
是否是 字符串s2
的子串
s1
叫做 pattern
模式串
暴力匹配都知道
abcde
前缀:a
ab
abc
abcd
abcde
真前缀不包括abced
后缀同理
- 所以我们求出
s1
的prefix[]
要后移一位,才是最长公共(真)前后缀
为什么prefix[]
要右移???
当不断比较s2[i]
与 prefix[j]
,相同就比较下一个,不同
就把pattern
串的上一个字符pattern[j-1]
(公共前后缀最大)的第一个后缀字符拿来与当前s2[i]
比较,所以prefix[j]
右移一位,j
位保存的是j-1
位的最长前后缀的 (第一个) 后缀下标
- 先求出
prefix[]
前缀表,最长公共的(真)前后缀
例如
怎么求prefix[]
- 不断的拿
i
与len
相比较,相同则 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; int len = 0; int i = 1; while ( i < n ) { if ( pattern[i] == pattern[len] ) { len++; prefix[i] = len; i++; } else { if ( len > 0 ) { len = prefix[len - 1]; } else { prefix[i] = len; i++; } } } }
void move_prefixtable(int prefix[],int n) {
for (int i = n-1; i > 0; i--) { prefix[i] = prefix[i-1]; } prefix[0] = -1; } void kmp_serach(char text[] ,char pattern[]) { int n = strlen(pattern); int* prefix = malloc( sizeof(int) * n); prefix_table(pattern,prefix,n); move_prefixtable(prefix,n);
int m = strlen(text); int i = 0, j = 0; while (i<m) { if ( j == n-1 && pattern[j] == text[i] ) { printf("Found at %d",i-j); j = prefix[j]; } if ( pattern[j] == text[i] ) { i++; j++; } else { j = prefix[j]; if ( j == -1 ) { i++; j++; } } } } int main() { char pattern[1e6]; char text[1e6]; scanf("%s%s",text,pattern); kmp_serach(text,pattern); return 0; }
|