C语言 数据结构与算法 KMP

1、KMP简介:

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。字符串的模式匹配实质上就是寻找模式串P是否在主串T 中,且其出现的位置。我们对字符串匹配的效率的要求越来越高, 应不断地改良模式匹配算法,减少其时间复杂度。可以应用于网络信息查找、文本中字符串的索引等。

2、KMP算法介绍:

(以下为个人理解,不保证所有人都理解我的学习想法,如果看不懂next优化及创建过程的文章末尾有详解链接)

已知两个字符串S,T;S为匹配串,T为模式串,要在S中找到是否有T,步骤如下:

假设现在S串匹配到 i 的位置,T串匹配到 j 的位置;                                                                                                                                  如果当前字符匹配成功(S[i]=T[j]);则继续匹配下一个字符 ;(i++,j++)                                                                                            如果当前字符匹配失败(S[i]!=T[j]);将模式串T的 j 标记移动到j-next[j]位继续匹配,i 标记位置不用改变;

(比较于暴力匹配引出next数组)对于KMP算法的核心就是制造next数组;可以使得暴力匹配提高效率:举一个例子:

如果S串为"aaaaaaab",T串为"aaaab",我们以暴力匹配的做法,在i=4,j=4时S[4]='a',T[4]='b',此时S[5]!=J[5];我们就需要将i,j标记向后移动一位再次去匹配,但事实上,由于T串的特殊性即T[0-3]='a',在第一次匹配中,S和T中前四个字符都相等了(‘a’),其实没必要再去反复的比较S[1~3]与T[0~3]的字符了。因此,我们就需要一个next数组来标记当S与T不匹配时,应该把T中的哪一个元素继续与i比较。

3、next[ ]:

对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠,我们举一个一般性的例子,假设T='abaabcaba';

在T中我们要找到当前字符T[j]的最大相等的前缀与后缀,长度为(k);

模式串abaabcaba
最大前后缀相等元素长度:001120123

例如:T[8] = 'a';它的前缀是T[0-2]='aba',后缀是T[6-8]='aba',结果为3;

但是对于最长的相同的前缀和后缀,我们要清楚在S与T不匹配时,标记J具体要移动多少,所以通过上面找最长的相同的前缀和后缀的方式,我们创建next数组。

模式串Tabaabcaba
j012345678
next[j]-100112012

next数组的创建:

#define imax 105
int next[imax];
void getnext(char *p)
{
	int lenp=strlen(p);
	next[0]=-1;
	int k=-1;
	int j=0;
	while(j<lenp-1)
	{
		if(k==-1||p[j]==p[k])	//找到前缀最长匹配度
		{
			j++;
			k++; 
			if(p[j]!=p[k])	//如果当前元素在前缀中不存在时,
			{
				next[j]=k;
			}	
			else 
		    	next[j]=next[k];	//KMP优化
		}
		else
		{
			k=next[k];
		}
	}
	return;
}

4、匹配过程:

还是以上面为例,如果S串为"aaaaaaab",T串为"aaaab",那么我们知道next[ ]数组应该为{-1,0,1,2,3};在i<strlen(S),j<strlen(T)的情况下,我们比较S与T,当i=4,j=4时;S[4]!=T[4];我们按照算法i不变,j=next[j] = 3,比较:S[4]=T[3]='a',S[5]!=T[4],j=next[j]=3;    S[5]=T[3]='a',S[6]!=T[4],j=next[j]=3;        S[6]=T[3]='a',S[7]=T[4]='b';  j=strlen(T);完成。也就是说在i从5-7时,一直在比较S中是否有‘ab’。匹配成功时,我们要求返回位置。

KMP匹配:

int KMP(char *s,char *p)
{
	int i=0;
	int j=0;
	int lens=strlen(s);
	int lenp=strlen(p);
	while(i<lens&&j<lenp)
	{
		//如果j=-1(表示第一次开始或者重新开始匹配),即失配于首字符
		if(j==-1||s[i]==p[j])
		{
			j++;
			i++;
		}
		else
		{
			//如果j!+-1,或者当前匹配失败 ,则 
			j=next[j]; // i不变,j=next[j] 
		}
	}
	if(j==lenp)
		return i-j;//匹配成功 返回位置
	else
		return -1; 
}

5、测试:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int t,i;
	char a[10005],b[10005];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s",a);
		scanf("%s",b);
		if(strlen(a)>=strlen(b))
		{
			getnext(b);
			if(KMP(a,b)>-1)
				printf("yes:%d\n",KMP(a,b));
			else
				printf("no:%d\n",KMP(a,b));
		}
		else
		{
			getnext(a);
			//getnext(b);
			if(KMP(a,b)>-1)
				printf("yes:%d\n",KMP(a,b));
			else
				printf("no:%d\n",KMP(a,b));
		}
		for(i=0;i<strlen(b);i++)
		{
			printf("%d ",next[i]);
		}
	}
	return 0;
}

附上一篇精致的解析KMP博客:

https://blog.csdn.net/v_july_v/article/details/7041827#t9

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C语言数据结构BF算法KMP算法的介绍: 1. BF算法(暴力匹配算法) BF算法是一种朴素的字符匹配算法,也称为暴力匹配算法。它的基本思想是从主串的第一个字符开始,依次与模式的每个字符进行比较,如果匹配成功,则继续比较下一个字符,否则主串指针后移一位,重新开始匹配。这种算法的时间复杂度为O(m*n),其m和n分别为主串和模式的长度。 以下是BF算法C语言实现: ```c #include <stdio.h> #include <string.h> int BF(char *s, char *p) { int i = 0, j = 0; int slen = strlen(s); int plen = strlen(p); while (i < slen && j < plen) { if (s[i] == p[j]) { i++; j++; } else { i = i - j + 1; j = 0; } } if (j == plen) { return i - j; } else { return -1; } } int main() { char s[] = "hello world"; char p[] = "world"; int pos = BF(s, p); if (pos != -1) { printf("匹配成功,位置为:%d\n", pos); } else { printf("匹配失败\n"); } return 0; } ``` 2. KMP算法(Knuth-Morris-Pratt算法KMP算法是一种改进的字符匹配算法,它的基本思想是利用已知信息来避免无效的比较。具体来说,它通过预处理模式,得到一个next数组,用于指导匹配过程的跳转。在匹配过程,如果当前字符匹配失败,则根据next数组的值进行跳转,而不是直接从主串的下一个字符开始匹配。这种算法的时间复杂度为O(m+n),其m和n分别为主串和模式的长度。 以下是KMP算法C语言实现: ```c #include <stdio.h> #include <string.h> void getNext(char *p, int *next) { int i = 0, j = -1; int plen = strlen(p); next[0] = -1; while (i < plen - 1) { if (j == -1 || p[i] == p[j]) { i++; j++; next[i] = j; } else { j = next[j]; } } } int KMP(char *s, char *p, int *next) { int i = 0, j = 0; int slen = strlen(s); int plen = strlen(p); while (i < slen && j < plen) { if (j == -1 || s[i] == p[j]) { i++; j++; } else { j = next[j]; } } if (j == plen) { return i - j; } else { return -1; } } int main() { char s[] = "hello world"; char p[] = "world"; int next[strlen(p)]; getNext(p, next); int pos = KMP(s, p, next); if (pos != -1) { printf("匹配成功,位置为:%d\n", pos); } else { printf("匹配失败\n"); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值