KMP算法----代码实现篇

通过学习kmp的思想和实现步骤,我们已经初步理解了kmp算法的运行过程,那么接下来必然要学会代码实现。现在,进行代码实现吧!

kmp主要就是两步:

第一步,求出前缀表,也就是常说的next数组;
第二步,进行具体的匹配过程。

首先,我们一起来用代码解决kmp算法中的前缀表吧!

我们再次回顾前缀表的构建方法(请移步到我们上一篇博客: KMP算法—讲解篇)。
这次我们会和上次讲的前缀表建立有一点点不同(但其实就是换汤不换药,我会慢慢说明的):
在这里插入图片描述
在这里插入图片描述
我们可以对比一下临近的两行,比如:
在这里插入图片描述
在此,假如我们已经获得了上图第一行的前缀表数据(当前是1),那么我们只需判断下一位B(p[6])是否和B(p[1])相同,如果相同的话,那自然而然这一行对应的前缀表数据就是1++了;如果不相同呢?
我们暂且不讨论不同的情况(狗头保命),我们先把现有的思路实现了。

//pattern是我们现有的模式串,prefix是我们要求的初代前缀表(因为没有移动),n是串的长度
void prefix_table(char pattern[],int prefix[],int n){
	prefix[0]=0;//不解释(只有一个元素的串嘛,翻一翻上面的那个每行对应的数字奥)
	int len =0;//len,是用来比较的长度,具体作用当你模拟几次就会了解。
	int i=1;//用来标是当前所求的位置下标
	while(i<n){//i<n的时候循环
		if(pattern[i]==pattern[len]){
                    //len就是上一次求完之后的公共串长度,判断pattern[len]和现在位置是否相同
			len++;//长度加1
			prefix[i]=len;//当前的前缀表数据就是加1之后的len
			i++;//往后移动一位
		}
		else {
			//这里就是我们发现两个不相同时的情况。
		}
	}
}

下来,我们讨论不相同时的情况。
在这里插入图片描述
看到这,可能就有疑问了:不是说这些都要往后移动一格吗?第一个不是添加-1吗?

这就是我在开头所说的那一点点不同(其实都是一样的),在这里用代码实现前缀表的过程中,我们先像这样对齐的建立,再都往后移动一格,就好了。

好了我们继续回到问题。
我们发现此时的len=3,i=8(假如我们已经进行到当前位置了)。明显pattern[3]和pattern[8]不相同,那么我们把len更新为prefix[len-1];len=1再次对比,发现pattern[len]和pattern[8]不相同,我们继续把len更新为prefix[len-1],len=0;发现pattern[len]和pattern[8]相同,所以len++,prefix[8]=1;
完善代码(包含移位的过程):

//pattern是现有的模式串,prefix是要求的初代前缀表(因为没有移动),n是串的长度
void prefix_table(char pattern[],int prefix[],int n){
	prefix[0]=0;//不解释(只有一个元素的串嘛,翻一翻上面的那个每行对应的数字奥)
	int len =0;//len,是用来比较的长度,具体作用模拟几次就会理解。
	int i=1;//用来标是当前所求的位置下标
	while(i<n){//i<n的时候循环
		if(pattern[i]==pattern[len]){
                    //len就是上一次求完之后的公共串长度,判断pattern[len]和现在位置是否相同
			len++;//长度加1
			prefix[i]=len;//当前的前缀表数据就是加1之后的len
			i++;//往后移动一位
		}
		else {
			//这里就是我们发现两个不相同时的情况。
                   //为了防止出现我们求prefix[1]的时候访问到pattern[-1]
                   if(len>0){
				len = prefix[len-1];
                            //正常情况错位更新len(因为是到了len-1嘛,所以称为len-1)
			}
			else {
				prefix[i]=len;//这里len就是0,所以写成0也是可以的
				i++;
			}
		}
	}
	//现在实现移位 
	for(i=n-1;i>0;i--){
		prefix[i]=prefix[i-1];
	}
	prefix[0]=-1;
}

到这里,已经简述了前缀表的建立,下面就是完整kmp算法的实现:

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
//pattern是现有的模式串,prefix是要求的初代前缀表(因为没有移动),n是串的长度
void prefix_table(char pattern[],int prefix[],int n){
	prefix[0]=0;//不解释(只有一个元素的串嘛,翻一翻上面的那个每行对应的数字奥)
	int len =0;//len,是用来比较的长度,具体作用模拟几次就会理解。
	int i=1;//用来标是当前所求的位置下标
	while(i<n){//i<n的时候循环
		if(pattern[i]==pattern[len]){
                    //len就是上一次求完之后的公共串长度,判断pattern[len]和现在位置是否相同
			len++;//长度加1
			prefix[i]=len;//当前的前缀表数据就是加1之后的len
			i++;//往后移动一位
		}
		else {
			//这里就是我们发现两个不相同时的情况。
                   //为了防止出现我们求prefix[1]的时候访问到pattern[-1]
                   if(len>0){
				len = prefix[len-1];//正常情况错位更新len(因为是到了len-1嘛,所以称为len-1)
			}
			else {
				prefix[i]=len;//这里len就是0,所以写成0也是可以的
				i++;
			}
		}
	}
	//现在实现移位 
	for(i=n-1;i>0;i--){
		prefix[i]=prefix[i-1];
	}
	prefix[0]=-1;
}
void kmp_search(char text[],char pattern[]){//主串text,模式串pattern 
	int n=strlen(pattern);
	int m=strlen(text);
	int *prefix=(int *)malloc(sizeof(int)*n);//建立一个前缀表数组,也即是next数组 
	prefix_table(pattern,prefix,n);//调用建立前缀表数组函数 
	int i=0,j=0,flag=0;
	//text[i]         ,len=m    用i和m表示主串的下标和长度 
	//pattern[j]      ,len=n    用j和n表示模式串的下标和长度 
	while(i<m){
		if(j==n-1&&text[i]==pattern[j]) {
			printf("We find it in %d\n",i-j);//输出模式串在主串中的位置 
			flag=1; 
			j=prefix[j];//匹配成功 ,我们可以直接退出,当然有些主串和模式串并不只在一个地方包含 
		}
		if(text[i]==pattern[j]){//正常匹配,往后+就行 
			i++;
			j++;
		}
		else {//匹配失败,失配位和前缀表中数据所指的项对齐 
			j=prefix[j];
			if(j==-1){
			//这就是我们访问到了那个开头的-1,这个时候,我们当然是让模式串往后移动一格,并且和主串的下一个元素匹配 
				i++;
				j++;
			}
		}
	}
	if(flag==0){
		printf("We can't find it in text\n");//flag=0,未找到 
	} 
}
int main()
{
	char pattern[]="ABABCABAA";
	char text[]="ABABABCABAABABABABCABAA"; 
	//char text[]="ABABABCAAABABABABCAAA"; 
	kmp_search(text,pattern);
	return 0;
 } 

以上就是本人对KMP浅显的理解,有不足之处希望大佬指正。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
BF算法和KMP算法都是串的模式匹配算法,但是它们的时间复杂度不同。BF算法的时间复杂度为O(m*n),其中m和n分别为主串和模式串的长度。而KMP算法的时间复杂度为O(m+n)。因此,当模式串较长时,KMP算法的效率更高。 下面是BF算法和KMP算法的介绍和演示: 1. BF算法(暴力匹配算法) BF算法是一种朴素的模式匹配算法,它的思想是从主串的第一个字符开始,依次和模式串的每个字符进行比较,如果匹配成功,则继续比较下一个字符,否则从主串的下一个字符开始重新匹配。BF算法的时间复杂度为O(m*n)。 下面是BF算法的Python代码演示: ```python def BF(main_str, pattern_str): m = len(main_str) n = len(pattern_str) for i in range(m-n+1): j = 0 while j < n and main_str[i+j] == pattern_str[j]: j += 1 if j == n: return i return -1 # 测试 main_str = 'ababcabcacbab' pattern_str = 'abcac' print(BF(main_str, pattern_str)) # 输出:6 ``` 2. KMP算法(Knuth-Morris-Pratt算法) KMP算法是一种改进的模式匹配算法,它的核心思想是利用已经匹配过的信息,尽量减少模式串与主串的匹配次数。具体来说,KMP算法通过预处理模式串,得到一个next数组,用于指导匹配过程中的跳转。KMP算法的时间复杂度为O(m+n)。 下面是KMP算法的Python代码演示: ```python def KMP(main_str, pattern_str): m = len(main_str) n = len(pattern_str) next = getNext(pattern_str) i = 0 j = 0 while i < m and j < n: if j == -1 or main_str[i] == pattern_str[j]: i += 1 j += 1 else: j = next[j] if j == n: return i - j else: return -1 def getNext(pattern_str): n = len(pattern_str) next = [-1] * n i = 0 j = -1 while i < n-1: if j == -1 or pattern_str[i] == pattern_str[j]: i += 1 j += 1 next[i] = j else: j = next[j] return next # 测试 main_str = 'ababcabcacbab' pattern_str = 'abcac' print(KMP(main_str, pattern_str)) # 输出:6 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值