BF与KMP算法详解~

串的模式匹配算法


简而言之就是给你两个字符串,一个主串,一个子串,我们写一个算法,看是否能在主串中找到子串,并且返回相匹配的子串第一个字符在主串中出现的位置!


BF

在这里插入图片描述

思路:
这个算法是一个比较暴力的算法
假设:
主串的长度 = n;
字串的长度 = m;
在这里插入图片描述
如图所示,i初始值为pos,j的初始值为0,分别拿 i对应的值与j对应的值相比较,如果相等,则i++,j++;直到j的值大于等于字串的长度,这个时候说明主串中有字串,此时返回i-j即可,如果j达到字串的长度之前,两个值就不相等,那么i = i-j+1,j = 0;拿之前i的第一个位置的下一个位置和字串重新比较。
假设第一次是从主串的第一个字符a开始比较,那么第二次就是拿b开始的主串与子串比较。一直比,直到j>=子串的长度或者i>=主串的长度。
时间复杂度
假设从主串的第i个位置开始与子串匹配成功,则在前i-1躺匹配中字符总共比较了(i-1)m次;若第i躺成功的字符比较次数为m,则总的比较次数时im,假设是从最后一个字符才匹配成功的,则i = n-m+1;
所以时间复杂度是(n-m+1)m,那么时间复杂度为O(mn);
代码如下:

#include<string>
#include<iostream>
using namespace std;
string s = "ababcabcacbab";
string a = "abcac";
int BF(string s1, string s2,int pos)//pos表示在主串的第pos位置开始查找
{
	int i = pos ,j = 0;//i表示主串的下标,j表示字串的下标
	if (s.size() == 0 && s2.size() == 0) return -1;
	if (pos < 0 || pos >= s1.size()) return -1;//养成好的代码习惯hh
	while (i < s1.size() && j < s2.size())
	{
		if (s1[i] == s2[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;//i-j表示主串中最开始和字串比较的下标 +1表示要用主串的下一个位置再重新和子串比较
			j = 0;
			
		}
	}
	if (j >= s2.size())
		return i - j;
	return -1;



}
int main()
{
	
	printf("%d\n", BF(s, a,0));//打印a子串在s串中的位置

	return 0;
}

KMP

时间复杂度 O(m*n)
思路
上述的BF算法中,如果子串与主串出现不匹配的情况,那么i需要改编成i-j+1,但是KMP算法不用改变i的值,主串的i只需要在与字串字符相等的时候i++,不需要回溯(就是i = i-j+1)并且上述BF算法中,当出现子串与主串不匹配时,子串的j会从头开始,这在KMP算法中也是改变的点。下面我用图讲解一下
首先:为什么主串可以不退?即i值不会减
在这里插入图片描述为什么可以这样?
在这里插入图片描述

引出next数组 next[j] = k,k表示的是当子串位于j时不匹配的时候j的后退值,即如果不匹配的时候j的重新取值。
在这里插入图片描述
next的目测法:
在这里插入图片描述
上面的是目测法,转化成代码的话我们还得分析一下:

在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<assert.h>
#include<string>
using namespace std;

string s, p;//s表示主串p表示子串
void Get_next(int* next, string p)
{
	//根据之前提到的,无论何种情况,next[0] = -1 next[1] = 0
	next[0] = -1;
	next[1] = 0;
	//next的下标从2开始
	int j = 2;
	int k = 0;//注意:此时相当于只知道next[j-1]的值,需要求的是next[j]的值
	          //所以我们需要比较的是next[j-1]与其对应的k
	         
	while (j < p.size())
	{

		if (k==-1||p[j - 1] == p[k])//因为一开始我们需要求的只是next[2]而恰好next[1]与j = 1对应的k值我们都知道
			                //所以这样写
		  //当第一个值都不符合的时候,k = -1.所以也应该满足条件
		{
			next[j] = k + 1;//满足p[j-1]==p[k]的时候
			k++;
			j++;
		}
		else
		{
			k = next[k];
		}





	}
}
int KMP(string s, string p, int pos)//表示主串从pos开始匹配
{
	if (s.size() == 0) return -1;
	if (pos >= s.size()) return -1;
	int* next = (int*)malloc(sizeof(int) * p.size());
	assert(next);//判断next不为NULL
	Get_next(next, p);//得到子串的next值
	int i = pos, j = 0;
	while (i < s.size() && j < p.size())
	{
		if (j == -1 || s[i] == p[j])//当next[j]使得j为-1的时候其实就是拿子串的第一个元素与主串比较
		{
			i++;
			j++;
		}
		else
			j = next[j];


	}
	if (j >= p.size())//跳出循环的有两种情况,当满足此条件时说明匹配成功
		return i - j;
	return -1;



}
//ababcabcacbab
//abcac
int main()
{
	cin >> s >> p;
	printf("%d\n", KMP(s, p, 0));
	return 0;
}


KMP实在是太抽象了,如果解释的不是很清楚还请见谅~~
求赞,求关注,求三连!!!

  • 42
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 36
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天少点debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值