- 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; }
|