字符串模式识别——BF算法与KMP算法

本文介绍了字符串模式匹配中的BF算法和KMP算法。BF算法是一种暴力匹配方法,时间复杂度为O(n*m),而KMP算法通过利用已匹配信息避免不必要的回溯,提高了效率。文章还详细讲解了KMP算法中的Next数组及其生成过程。

模式匹配

BF算法

暴力解
1.两串的起始下标为0
2.循环结束的条件:子串遇到\0→成功返回位置,主串遇到\0(长度减子串长度的位置) →失败
3.比较:s = t,下一位; s != t 回溯
4.回溯:s的指针i回到主串i-j+1 位置 j回到子串头部

时间复杂度
S长度:n T长度:m

最好情况:不成功的匹配都发生在T的第一个字符
不成功趟数的比较次数----1*(i-1)次
成功趟数的比较次数----m次
所有匹配成功的可能情况有n-m+1
(n+m)/2
O(n+m)

最差情况:不成功的匹配都发生在T的最后一个字符
不成功趟数的比较次数----m*(i-1)次
成功趟数的比较次数----m次
一共比较了i*n次
m(n-m+2)/2
O(n*m)

KMP算法

KMP改进了BF的回溯算法,利用了已经部分匹配的结果,主串中的i不回溯,只是子串滑动,省去了没必要的回溯,大大提高了效率。

分清:前缀后缀 前缀后缀相当于一个“标识”
T移动位数 = 子串已匹配的字符数 - 失配位置前的最长前缀匹配字符数
j对齐位置:失配位置前的最长前缀匹配字符数+1 - 1(+1是前缀的后一位 -1是数组从0存储)

next数组的三种取值

  • 当j = 0时, next[j]=-1 不进行字符比较 此时要做i++ j++ (同前面比较:s[] = t[]的情况)
  • 当j > 0 时, next[j]的值为:前缀后缀相同的长度
  • 当前缀后缀相同的长度为0时,next[j]=0 回到头部开始比较

Next数组

next数组的获取极难理解

建议边看void getNext(char* T, int* n)边完成以下表格:

ij位置j起始位置i起始位置
ij对应的值-10123456
T串abcdabd
next数组-1起始值
void getNext(char* T, int* n)//只对子串T进行处理 
{
	int lenT = strlen(T);
	int j = -1;//j是前缀 
	int i = 0;//i是后缀
	n[0] = -1;//next数组
	while (i < lenT)//i在T内
	{
		if (j == -1 || T[i] == T[j])
		{
			i++; j++;
			n[i] = j;//这里的i j是已经加一的值
		}
		else
		{
			j = n[j];
		}
	}
}

运行完上面代码表格如下:

ij位置j最终位置i最终位置
ij对应的值-10123456
T串abcdabd
next数组-1起始值000011

源代码

#include <iostream>
#include <cstring> 
using namespace std;

int BF(char S[], char T[])
{
	int i = 0, j = 0;
	while (S[i] != '\0'&& T[j] != '\0')//可以直接写0   S[i] && T[j]
	{
		if (S[i] == T[j])
		{
			i++; j++;
		}
		else
		{
			i = i - j + 1; j = 0;
		}

	}

	if (T[j] == '\0')//成功  
		return i - j;//从1开始就是 i-j+1 

	if (S[i] == '\0')//失败  
		return 0;
}

void getNext(char* T, int* n)//只对子串进行处理 
{
	int lenT = strlen(T);
	int j = -1;//j是前缀 
	int i = 0;//i是后缀
	n[0] = -1;
	while (i < lenT)
	{
		if (j == -1 || T[i] == T[j])
		{
			i++; j++;
			n[i] = j;//这里的i j是已经加一的值
		}
		else
		{
			j = n[j];
		}
	}
}

int KMP(char S[], char T[])
{
	int i = 0, j = 0;
	int lenS = strlen(S);
	int lenT = strlen(T);
	int next[100];//不能用lenT去初始化数组
	getNext(T, next);//传入地址,数组名就是地址
	//while(S[i] != '\0'&& T[j] != '\0')//不能用 '\0',因为j=-1数组越界 
	while (i < lenS - lenT + 1 && j < lenT)
	{//可以计算出字符串长度 strlen 
		if (j == -1 || S[i] == T[j])
		{
			i++; j++;
		}
		else
		{
			j = next[j];
		}

	}

	if (j == lenT)//成功  
		return i - j;//若数组从1开始就是 i-j+1 

	if (i == lenS - lenT + 1)//失败  
		return 0;
}

int main()
{
	char S[] = "ahksafafhjehfjkhfkjahkjhrjkfhksbvbfsjvb";
	char T1[] = "jkhfkja";
	char T2[] = "axkahdh";
	if (KMP(S, T1)) { cout << "成功,位置是:" << BF(S, T1) << endl; }
	else { cout << "没有找到匹配值"; }

	if (KMP(S, T2)) { cout << "成功,位置是:" << BF(S, T2) << endl; }
	else { cout << "没有找到匹配值" << endl; }

	system("pause");
	return 0;
}

结果

在这里插入图片描述
严格来说,若数组从1开始应该是14位,改下输出就好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值