字符串匹配算法:BF算法 && KMP算法

字符串匹配算法

本章重点:

1、暴力匹配(BF)算法

2、KMP算法

BF算法

百度百科:

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

时间复杂度为O(M*N)。

假定给出字符串"ababcabcdabcde",然后给出子串"abcd",现在查找子串是否在主串中出现,出现返回主串中的第一个匹配的下标,失败返回-1。

字符串匹配算法:BF算法 && KMP算法_第1张图片

找到了,最后返回子串在主串中的起始位置 i-j

/*
字符串匹配算法
str:主串
sub:子串
返回值:返回子串在主串中的起始下标,如果不存在返回-1
*/

int BF(const char* str, const char* sub)
{
	assert(str != NULL && sub != NULL);
	if (str == NULL || sub == NULL)
	{
		return -1;
	}

	int i = 0; // 主串下标
	int j = 0; // 子串下标
	int lenstr = strlen(str);
	int lensub = strlen(sub);

	while (i < lenstr && j < lensub)
	{
		if (str[i] == sub[j])
		{
			++i;
			++j;
		}
		else
		{
			i = i - j + 1;
			j = 0;
		}
	}

	if (j == lensub)
	{
		return i - j;
	}
	return -1;
}

KMP算法

百度百科:

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

区别:KMP和BF唯一不一样的地方在,主串的i并不会回退,j也不会回到0号位置。

字符串匹配算法:BF算法 && KMP算法_第2张图片

next数组

保存子串某个位置匹配失败后,回退的位置。

KMP算法的精髓就是 next数组:也就是next[j] =k来表示,不同的j对应1个k值,这个K就是将来要移动的j要移动的位置。

K的求解:

1、规则:找到匹配成功部分的两个相等的真子串,一个以下标0开始,另一个以j-1下标结尾。

2、不管什么数据next[0]=-1; next[1]=0;

运用上面的思想,可以得出以下数组的next数组。
字符串匹配算法:BF算法 && KMP算法_第3张图片

推导
字符串匹配算法:BF算法 && KMP算法_第4张图片

如果p[i]==p[k],next[i+1]=k+1;

反之,继续回退去找,k=next[k]

当回退到0下标,k==-1时,next[i+1]=0;也是next[i+1]=k+1;

字符串匹配算法:BF算法 && KMP算法_第5张图片

字符串匹配算法:BF算法 && KMP算法_第6张图片

void GetNext(const char* p, int* next)
{
	assert(p != NULL && next != NULL);
	int lenp = strlen(p);

	next[0] = -1;
	next[1] = 0;
	int i = 2; // 子串下标
	int k = 0; 

	while (i < lenp)
	{
		if (k==-1 || p[i] == p[k])
		{
			next[i] = k + 1;
			++i;
			++k;
		}
		else
		{
			// p[i]!=p[k]
			// 回退k
			k = next[k];
		}
	}
}

/*
 str: 主串
 sub: 子串
 pos: 代表从主串pos位置开始匹配
 */
int KMP(const char* str, const char* sub, int pos)
{
	assert(str != NULL && sub != NULL);
	int lenstr = strlen(str);
	int lensub = strlen(sub);
	if (lenstr == 0 || lensub == 0) return -1;
	if (pos < 0 || pos >= lenstr) return -1;

	// next数组
	int* next = new int[lensub];
	assert(next);
	GetNext(sub, next);

	int i = pos; //遍历主串
	int j = 0; // 遍历子串
	while (i < lenstr && j < lensub)
	{
		// j=-1,一开始就匹配不到
		if (j == -1 || str[i] == sub[j])
		{
			++i;
			++j;
		}
		else
		{
			// !=
			j = next[j];
		}
	}
	
	if (j == lensub)
		return i - j;

	return -1;
}

注意点

注意j==-1情况,

字符串匹配算法:BF算法 && KMP算法_第7张图片

nextval数组(优化)

next数组的优化,即如何得到nextval数组

有如下串:

字符串匹配算法:BF算法 && KMP算法_第8张图片

  • 如果回退到的位置和当前字符一样,就写回退到的位置的nextval值
  • 如果回退到的位置和当前字符不一样,就写当前字符原来的nextval值

字符串匹配算法:BF算法 && KMP算法_第9张图片

你可能感兴趣的:(【算法】,算法,leetcode,数据结构,KMP)