串匹配算法,BF算法&KMP算法

串匹配算法,BF算法&KMP算法


串匹配: 在一个串(字符串)中找存不存在另一个串(字符串),并返回找到的位置。

C库中的方法: char * strstr(const char * S, const char *P);

BF算法 朴素算法

该直接穷举算法从字符串S的每一个字符开始查找,看字符串T是否会出现。
给S和T都有一个指针,开始时S[0]与T[0]比较,若相同则继续向后比较,若出现不同,则T[0]继续与S[i]比较(i为上次出现不同时S指针的位置),直到主串结束,或者匹配成功。
例:S=“ababcabcacbab” T=“abcac”
在这里插入图片描述

在这里插入图片描述

代码实现

// O(m*n)
int BF(char *s, char *p, int pos)
{
	if(s == NULL || p == NULL || pos < 0) return -1;

	int lens = strlen(s);
	int lenp = strlen(p);

	if(lens - pos < lenp) return -1;//判断主串

	int i = pos;
	int j = 0;

	while(i < lens && j < lenp)
	{
		if(s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;//回到原始位置+1
			j = 0;//j回到起始位置
		}
	}//两个串 某一个到了结尾

	if(j == lenp)//如果是j到达了结尾,那么返回的i-j就是主串中匹配合适的起始位置
		return i - j;
	return -1;//匹配失败
}

KMP算法

相比较起来,BF算法是一种暴力的求解方法。

如果出现这种类型的两个字符,就会出现大量的比较,使得算法效率很低

而KMP算法就解决的这类问题

首先我们需要一个叫前缀表(piefix table)的东西,将按下图所示的方法找到对应的最大公共前后缀,舍弃最后一个,且第一位下标记-1
在这里插入图片描述
在这里插入图片描述
然后继续进行比较,且是从挪动下标位置开始,即上图下标1处开始
在这里插入图片描述
在这里插入图片描述
若如果用BF算法对刚才我们举例的极端例子进行查找,会继续非常多次的比较,而用KMP算法
在这里插入图片描述
在这里插入图片描述
节省了很多次的比较,仅用七次就可以匹配完成

代码实现

在这里插入图片描述

在GetNext函数中,我们假如已经知道前缀表中下标为5的地方为1,而要求下标为6的地方为多少时,只需要比较对应的数字,前缀中A后面是B,所以最后缀在,在最后一个A后面加上一个B,那么6下标出的数字就是前一个2,通过最长前后缀的性质可以这样进行比较得到下一个的下标。
在这里插入图片描述
如果后缀A后面不是B,则根据当前len的下标,指向前一个的下标对应的前缀表,继而len指向的是下标1
在这里插入图片描述
在这里插入图片描述
然后继续进行比较,重复一开始的操作。注意上图的示例最终需要向右全部移动一格,且next[0] == -1

void GetNext(char *p, int *next)//实现前缀表
{
	next[0] = -1;//0 下标的最大公共前后缀为 -1

	int lenp = strlen(p);
	if(lenp < 2) return;

	next[1] = 0;//1 下标的最大公共前后缀为 0
	int i = 1, len = 0;
	//开始比较就是从两个数字的时候开始计算它的最大公共前后缀,因为一个数字必然为0,p[0]与p[1]比较

	while(i + 1 < lenp)//到倒数第二个下标时结束
	{
		if(len == -1 || p[i] == p[len])
		{
			next[++i] = ++len;
		}
		else
		{
			len = next[len];
		}
	}
}

int KMP(char *s, char *p, int pos)
{
	if(s == NULL || p == NULL || pos < 0) return -1;

	int lens = strlen(s);//s 是主串
	int lenp = strlen(p);

	if(lens - pos < lenp || lenp == 0)  return -1;

	int i = pos;
	int j = 0;
	
	//next 就是前缀表(piefix table)
	int *next = (int *)malloc(sizeof(int) * lenp);//申请 p 串空间大小的指针
	GetNext(p, next);


	while(i < lens && j < lenp)
	{
		if(j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}

	free(next);

	if(j == lenp)
		return i - j;
	return -1;
}

推荐大家去b站看一个up叫“正月点灯笼”关于KMP算法的讲解

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值