- 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[]
前缀表,最长公共的(真)前后缀
例如
aa | 1 |
---|---|
aba | 1 |
abc | 0 |
abcda | 1 |
怎么求prefix[]
- 不断的拿
i
与len
相比较,相同则prefix[j]=++len;
- 不同 就 找
len = prefix[len - 1]
前一个字符的前子缀的最后一个字符与pattern[i]
相比较,一直循环到相同即可,
prefix[]
求出后,开始拿模式串进行匹配
- 遇到不同则 比较
pattern
串的上一个字符的后缀字符, - 如果模式串上一个字符的后缀字符是-1(此时模式串在第一个字符
pattern[0]
),仍然将-1
移到s2[i]
这点来,但是不比较,那么数组模拟就是同时i++ j++
去比较下一个,(实际效果与移动的效果是一样的)
// 字符串最长公共前后缀
#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;
}