KMP算法 c++

我不认为以我乏力的描述能讲清楚KMP算法,所以我在这里推荐两个大佬写的博客,以供我自己遗忘时再次复习。

NEXT数组的理解:

https://blog.csdn.net/lee18254290736/article/details/77278769

作者:JensLee

KMP算法总体思想:

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

作者:阮一峰老师

编程思路:

https://blog.csdn.net/ebowtang/article/details/49129363

作者:EbowTang

什么时候对KMP的理解火候到了,我也写一篇有关KMP的详解,哼?

想了想,虽然有些地方我确实没法讲,但是还是尽量把我自己理解的放上去吧,下次忘了也好快速复习。

先放我的next数组:

vector makeNext(string P)  //构造NEXT数组
{
	int k = 0;
	int size = P.size();
	vector Next(size);
	Next[0] = 0;               //给第一个元素赋值为0
	for (int i = 1; i < size; i++)
	{
		while (k > 0 && P[i] != P[k])  //精髓所在,判断是否继续相等
		{
			k = Next[k-1];
		}
		if (P[i] == P[k])  如果相等则最长相等子串长度加1
		{
			k++;
		}
		Next[i] = k;
	}
	return Next;
}

        Next数组的求解是KMP算法中最难理解的地方也是最巧妙的地方, 虽然在大二学数据结构的时候为了应付考试学会了一个计算Next数组的小技巧,但是却完全没有理解它的精髓,当然,现在也仅仅是一只脚迈入了KMP算法的台阶,现在我就来解释一波Next数组的求解过程。

       首先需要知道一个概念,那就是前缀串和后缀串。什么是前缀?前缀就是除去最后一个元素以外的串。

       如“abcd”,前缀即为“abc”或“ab”或“a”,要注意的是前缀串必须从第一个元素开始,但长度不一定需要最长,同理可以理解一下后缀串。

      在这里先一句话概括Next数组里的元素都是些什么东西,不说明白这个后面讲它的算法估计也很难理解。一句话概括:Next数组里的元素如Next[i]的值的含义是前i+1个元素前缀串和后缀串最大的相等长度。举个栗子:

      如模式串P="abcdabd",则前缀串一共有如下可能,''a","ab","abc","abcd","abcda","abcdab" 共六种。

      后缀串也是六种可能,结果如下,"bcdabd","cdabd","dabd","abd","bd","d"。

      此时求Next[6]的值即为求串P的前7个元素前缀串和后缀串相等的最大长度,在这里没有一个相等的,所以是0,Next[6]=0;

     Next[5]的值为前5+1=6个元素前缀串和后缀串相等的最大长度,前缀串如下''a","ab","abc","abcd","abcda"。后缀串如下:

"bcdab","cdab","dab","ab","b",相等的最大前后缀串为"ab",则长度为2,Next[5]=2。依此类推,Next[0]需要我们自己赋值为0。

       问题在于,电脑不是人,必须要将规律抽象出来程序才能正常运行,同时时间复杂度还不能太大。。导致上述的求Next数组的函数我理解了挺久的,特别是下面这几行代码。

    while (k > 0 && P[i] != P[k])  //精髓所在,判断是否继续相等
    {
	k = Next[k-1];
    }
    if (P[i] == P[k])
    {
	k++;
    }

      这个地方我真解释的很乏力,所以我索性还是不解释了吧。大概就是对两种情况的分别计算,一种是继续相等,一种是不再相等。

     然后放我的kmp的函数:

void KMP(string S, string P)  //KMP算法
{
	vector Next = makeNext(P);
	int k = 0;                        //模式串下标
	for (int i = 0; i < S.size();)  //
	{
		if (S[i] == P[k])  //如果比较结果相等各往后移一位
		{
			if (k == P.size()-1)  //判断是否找到子串
			{
				cout <<"Index:"<< i - k<<" ";
				k = 0;
				continue;
			}
			k++;
			i++;
		}
		else if(S[i] != P[k]&&k>0)  //如果比较结果不等且比较的不是第0位模式串,将k移到Next[k-1]的位置
		{
			k =Next[k-1];
		}
		else if(S[i] != P[k] && k==0)  //比较结果不等且比较的为第0位模式串,各向后移动一位
		{
			k=0;
			i++;
		}
	}
}

我的程序如下:

#include 
#include
#include
#include
using namespace std;
vector makeNext(string P)  //构造NEXT数组
{
	int k = 0;
	int size = P.size();
	vector Next(size);
	Next[0] = 0;
	for (int i = 1; i < size; i++)
	{
		while (k > 0 && P[i] != P[k])  //精髓所在,判断是否继续相等
		{
			k = Next[k-1];
		}
		if (P[i] == P[k])
		{
			k++;
		}
		Next[i] = k;
	}
	return Next;
}

void KMP(string S, string P)  //KMP算法
{
	vector Next = makeNext(P);
	int k = 0;                        //模式串下标
	for (int i = 0; i < S.size();)  //
	{
		if (S[i] == P[k])  //如果比较结果相等各往后移一位
		{
			if (k == P.size()-1)  //判断是否找到子串
			{
				cout <<"Index:"<< i - k<<" ";
				k = 0;
				continue;
			}
			k++;
			i++;
		}
		else if(S[i] != P[k]&&k>0)  //如果比较结果不等且比较的不是第0位模式串,将k移到Next[k-1]的位置
		{
			k =Next[k-1];
		}
		else if(S[i] != P[k] && k==0)  //比较结果不等且比较的为第0位模式串,各向后移动一位
		{
			k=0;
			i++;
		}
	}
}


int main()
{
	string S;
	string P;
	cout << "输入S串:";
	cin >> S;
	cout << "输入P串:";
	cin >> P;

	cout << "KMP index:";
	KMP(S, P);
	cout << endl;
	return 0;
}

写到这发现我解释的可真的乏力( ̄_ ̄|||),什么时候有补充我再更吧(不存在的)

尴尬,在刷leedcode 28实现strstr()的时候,本来想看看我自己写的这篇文章加快回忆KMP算法的,但是却很尴尬地发现我给的代码里有一个bug,难怪之前测试这个kmp算法的时候偶尔会很诡异地出问题,我现在改完之后应该是没有问题了。

然而我还是无法彻底理解求Next数组的代码。。枯了

你可能感兴趣的:(算法,KMP,c,子串查找)