KMP

本文深入讲解了KMP算法的工作原理及实现过程,包括next数组的构造方法及其在字符串匹配中的应用,通过实例帮助读者理解算法背后的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

KMP算法:消除了BF算法的主串指针在相当多个字符比较相等后,只要有一个字符比较不相等便需要回退的缺点。

KMP算法的主要思想:设s为主串,t为模式串,j为s串当前比较字符的下标,k为t串当前比较的字符的下标,令i和j 的初值为0。

          1,当 s(j)==t(k)时,j和k分别增1之后再比较。

          2,当不相等时,j不变,k改变为next[k]之后再比较。

next数组:存储的是子串匹配时的一个回溯的位置。

                  0    1    2     3    4    5    6    7   8    9  10   11   12  13

例如:        a    b    a    b    c    a    b    c    d    a    b    c    d    e         j

next数组    -1   0   0    1    2    0    1    2    0   0     1    2    0    0

next数组值的确定规则:在子串中存在两个真子串相等,并且一个以0位置开始,一个以(j-1)位置结束

      next[0]=-1        next[1]=0  这两个是固定的,不变的

      next[2]:看串“ab”,根据next数组值确定规则,可以确定不存在两个相等的子串,所以next[2]=0

      next[3]:看串“aba”,根据next数组值确定规则,可以确定有相同的串“a”,所以next[3]=1

     next[4]:看串“abab”,根据next数组值确定规则,可以确定有相同的串“ab”,next[4]=2

     next[5]:看串“ababc“,根据next数组值确定规则,可以确定没有相同的串,next[5]=0

    。。。。。。。。

    按照这个规则,可以写出next数组

得到next数组的代码:

void GetNext(char *sub,int next[])
{
	//求子串的next数组
	int  j=0,k=-1;
	int lenth=strlen(sub);
	next[0]=-1;
	next[1]=0;
	while(j<lenth)
	{
		if(sub[j]==sub[k] || k==-1)
		{
			next[++j]=++k;
		}
		else
		{
			k=next[k];
		}
	}
}

一脸懵逼,是不是。。。上述代码就是用来求解模式串中每个位置的next[]值。

下面具体分析,我把代码分为两部分来讲:

(1):i和j的作用是什么?

i和j就像是两个”指针“,一前一后,通过移动它们来找到最长的相同真前后缀

(2):if...else...语句里做了什么?


假设i和j的位置如上图,由next[j] = k得,也就是对于位置i来说,区段[0, j - 1]的最长相同真前后缀分别是[0, k - 1]和[j - k, j - 1],即这两区段内容相同

按照算法流程,if (P[j] == P[k]),则next[++j] = ++k;;若不等,则k = next[k],见下图:


next[k]代表[0, k - 1]区段中最长相同真前后缀的长度。如图,用左侧两个椭圆来表示这个最长相同真前后缀,即这两个椭圆代表的区段内容相同;同理,右侧也有相同的两个椭圆。所以else语句就是利用第一个椭圆和第四个椭圆内容相同来加快得到[0, j- 1]区段的相同真前后缀的长度。

细心的朋友会问if语句中k== -1存在的意义是何?第一,程序刚运行时,k是被初始为-1,直接进行P[j] == P[k]判断无疑会边界溢出;第二,else语句中k= next[k],k是不断后退的,若k在后退中被赋值为-1(也就是k = next[0]),在P[j] == P[k]判断也会边界溢出。综上两点,其意义就是为了特殊边界判断。

  完整代码:

#include<stdio.h>
#include<string.h>
void GetNext(char *sub,int next[])
{
	//求子串的next数组
	int  j=0,k=-1;
	int lenth=strlen(sub);
	next[0]=-1;
	next[1]=0;
	while(j<lenth)
	{
		if(sub[j]==sub[k] || k==-1)
		{
			next[++j]=++k;
		}
		else
		{
			k=next[k];//如果
		}
	}
}
int KMP(char *str,char *sub,int next[])
{
	int i=0;
	int j=0;
	int stlen=strlen(str);
	int sublen=strlen(sub);
	GetNext(sub,next);
	while(i<stlen && j<sublen)
	{
		if(str[i]==sub[j] || j==-1)
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];
		}
	}
	if(j==sublen)
	{
		return i-j;
	}
	return -1;
}
void main()
{
	char str[]="abcababcabc";
	char sub[]="abcabc";
	int next[10]={0};
	int index=KMP(str,sub,next);
	for (int i = 0; i < strlen(sub); ++i)
    {
        printf("%d  ",next[i]);
    }
	 printf("\n");
	 printf("%d\n",index);
    
}


         



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值