KMP算法(字符串匹配问题)

"本文详细讲解了如何使用KMP算法解决给定源字符串source和模式串pattern匹配问题,包括next数组的计算与应用,以及如何通过next数组确定匹配失败时的回退位置。通过实例展示了如何在源字符串"aabaabaaf"中找到模式串"aabaaf"的首次出现位置3。"
摘要由CSDN通过智能技术生成

-------------------------------------------------预备知识-----------------------------------------------

1、字符串前缀

对于字符串str = aabbcd,其前缀为{a, aa, aab, aabb, aabbc}

2、字符串后缀

同样对于上述字符串,其后缀为{d,cd,bcd, bbcd, abbcd}

3、字符串最长相等前后缀

对于字符串str = aabbcd,我们从上面可以知道,其没有相同的前缀和后缀,故对于该字符串,它的最长相等前后缀的值为0;
而对于字符串str = aabaa,其前缀为{a, aa, aab, aaba},其后缀为{a, aa, baa, abaa},可以知道,他有相同的前后缀aaa。所以该字符串的最长相等前后缀的值为 2

-------------------------------------------------正文开始--------------------------------------------------
问题描述:

给定一个源字符串source,以及一个模式串pattern,求模式串在源字符串出现的第一个位置。如何源字符串中不包含模式串,则返回 -1

例如:

源字符串为string source = "aabaabaaf"
模式串为string pattern = "aabaaf"
则应该返回源字符串包含模式串的第一个位置 3

在这里插入图片描述

对于上图可以知道,当我们匹配到第六个字符的时候,发现不匹配,这时候就需要回退指向模式串的ppointer指针,然后进行重新匹配。至于指针应该回退到什么位置,看下面描述。

4、next[]数组求解

KMP算法的核心就是求next[ ]数组问题。

其中next[]数组存的就是当发生不匹配的时候,ppointer指针应该回退的下标位置。这时候,就用到了上述提到的最长相等前后缀。

next[i]表示的是在下标 i 之前的字符串,其最长相等前后缀的长度。
如,对于字符串string source = "aabaabaaf"
next[0] = 0 因为在0下标之前没有字符串
next[1] = 0 因为'a'不存在前后缀
next[2] = 1 最长相等前后缀为'a'
next[3] = 0 字符串为'aab'无相等前后缀
同理可以知道
next[4] = 1
next[5] = 2
next[6] = 3
next[7] = 4
next[8] = 5

next[]数组求解过程:

  • 1、初始化next[0] = 0; j = 0;j表示前缀末尾)
  • 2、当前缀末尾和后缀末尾不相等时,回退jj = next[j-1]
  • 3、当前缀末尾和后缀末尾相等时,前缀末尾j往前移动
  • 4、更新next数组

下面进行代码的实现:
(下面的i表示前缀末尾,j表示后缀末尾)

void getNext(vector<int>&next, const string& s)
{
	int n = next.size();
	//初始化j和next数组
	int j = 0; //代表前缀末尾
	next[0] = 0;
	//i从1开始是因为i表示的是后缀末尾,而要存在后缀末尾,则i不能为0,因为i为0的话不存在后缀
	for(int i = 1; i < n; i++)
	{
		//当前缀末尾和后缀末尾不相等时,回退j
		while(j > 0 && s[i] != s[j])
		{
			j = next[j-1];
		}
		//当前缀末尾和后缀末尾相等时,前缀末尾前移
		if(s[i] == s[j])
		{
			j++;
		}
		//更新next数组
		next[i] = j;
	}
}

5、KMP字符串匹配

在求解完next[]数组之后,我们可以根据next数组来确定当ppointer与spointer不匹配时,ppointer应该回退的位置了。
然后求解上述问题的过程如下:

  • 1、用i来表示源字符串的位置,j表示模式串的位置
  • 2、若源字符与模式串字符不匹配,回退j
  • 3、若源字符串与模式串字符匹配,j前移
  • 4、判断j是否等于模式串长度,若是,则匹配结束,否则i++,继续匹配

代码如下:

class sulotion
{
public:
	void getNext(vector<int>&next, const string& s)
	{
		int n = next.size();
		//初始化j和next数组
		int j = 0; //代表前缀末尾
		next[0] = 0;
		for(int i = 1; i < n; i++)
		{
			//当前缀末尾和后缀末尾不相等时,回退j
			while(j > 0 && s[i] != s[j])
			{
				j = next[j-1];
			}
			//当前缀末尾和后缀末尾相等时,前缀末尾前移
			if(s[i] == s[j])
			{
				j++;
			}
			//更新next数组
			next[i] = j;
		}
	}

	int KMP(const string& source, const string& pattern)
	{
		int n = pattern.size();
		vector<int>next(n);
		getNext(next,  pattern);
		int j = 0, i = 0;
		for(; i < source.size(); i++)
		{
			while(j > 0 && source[i] != pattern[j])
			{
				j = next[j-1];
			}
			if(source[i] == pattern[j])
			{
				j++;
			}
			if(j == n)
			{
				return i - j + 1;
			}
		}
		return -1;
	}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值