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[]前缀表,最长公共的(真)前后缀
    例如
aa1
aba1
abc0
abcda1

怎么求prefix[]

  • 不断的拿ilen相比较,相同则 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值