C++ KMP算法实现与分析

#include
using namespace std;
/*kmp算法基本思想,当目标串与主串匹配过程中出现相对于目标串上某个位置不与主串匹配时,此时一般不需要回退相对于主串的位置到原来相对于主串开始匹配的位置的下一个位置并把相对于目标
串的匹配位置归 0(这是最原始的算法);大概率可以保持主串上将要匹配的位置不变,把目标串上将要匹配的位置回退至目标串上前面某个位置上继续在这个位置上开始匹配;而这个位置索引就是
前面已经完成匹配的那部分目标串的子串的最大公共前缀和后缀的长度【公共前后缀=>不算已经匹配的整个子串】;=>即只需要把目标串上从这个长度做为索引的位置开始与主串上现在将要匹配的位置(一般主串将要匹配的位置索引不变)开始匹配就可以了

现在讲唯一驱动主串上将要匹配的位置前进的情况:那就是主串上当前要匹配的位置与目标串上0位置都不匹配 =>此时主串上将要匹配的位置才前进到下一个位置    【当然正常成功匹配的时候主串与目标串各自将要匹配的位置都会前进,这里不必讨论这种正常成功匹配的情况】

这样我们就知道当相对于目标串上的某个将要位置发生不匹配的时候,这个将要匹配的位置将会改变【即回退】的位置的索引就是公共最大前后缀的长度;这个值只与目标串有关于主串无关
使用next数组 【其实我感觉称为prev数组更贴切】就是一张记录 如果目标串上某一个位置发生不匹配的时候(数组索引),这个位置的回退位置的索引是多少=>也就是前面匹配部分的最大公共前后缀的长度(数组值)
*/

/*实现方法
	next[0]=-1;//第0位就出现不匹配,是少有的需要把主串上的将要匹配位置后移一位,目标串上从0位置开始去匹配主串;从逻辑上看相当于从-1的位置即目标串上0位置的前一个位置开始匹配主串的原来将要匹配的位置
	因为这是一个无效的位置所以真正匹配的位置是主串上的原来将要匹配位置后移一位,目标串上从0位置开始去匹配主串;其实这个值不必设置为-1,一个特殊值即可,但是为了与逻辑上切合,以及代码写法上与KMP正常匹配szSrc[iSrc] == szDest[iDest]或者getnext的尾后判断 szDest[k] == szDest[i]后的处理兼容方便一般就设为-1
	语义就是:第0位不匹配,需要把主串上的将要匹配位置后移一位,目标串上从0位置开始去匹配主串;

	首先匹不匹配在目标串给定情况下是由主串决定的,与目标串无关;
	而目标串某个位置不匹配时,目标串回退将要匹配位置的索引是由目标串本身决定的与主串无关=>所以目标串上某个位置不匹配时对应的将要回退的匹配位置的索引是可能根据其前一个位置如果
	不匹配时对应的绘图位置的索引【公共前后缀的长度】推出的
*/
int* getNext(const char* szDest,int*next)//只与目标串相关
{
	next[0] = -1;//参见next[0]语义

	int i = 0;
	int k = -1;

	//从1~strlen(szDest)-1索引位置对应的目标串回退位置

	/*这里比较难理解的是i与k的语义
		首先要知道此时i位置如果不匹配时对应的回退索引位置是已经推导出来的,这里是要推导i的下一个位置如果出现不匹配时对应的回退索引位置

		这里的k的含义其实有点复杂,分二种情况 一种是判断成功进入if分支 ++k 之后的k =>此时代表的就是i的下一个位置若不匹配时的回退索引位置
		而在这++之前的k,包括else分支的部分以及szDest[k] == szDest[i]中被当作索引的部分就需要想象力了!容我组织一下词汇

		通过变更逻辑主串与目标串找i下一个位置不匹配时的最大公共前后缀长度
		把索引从0到索引为k的子串做目标串,把索引从i-k到索引为i的子串做主串;并且认为目标串中除了最后一个位置k 与主串中处理最后一个位置 i都已经完全匹配
		现在如果 szDest[k] == szDest[i] 就可以认为 i的下一个位置的最大公共前后缀长度为  k+1;如果不考虑else分支的情况我们确实可以理解成这时的k就是i位置
		不匹配时对应的回退位置索引(或者其公共最大前后缀长度);如果考虑else分支则就是刚刚讲到的变更逻辑主串和逻辑目标串后的逻辑目标串k位置不匹配时对应的
		回退位置;目的就是为了找到i位置的下一个位置的回退位置 =>因为从抽象层来说公共前后缀肯定是最前或者最后算的,所以else分支的情况下k = next[k];肯定比
		szDest[k] == szDest[i]直接就匹配的最大公共前后缀长度小;最终匹配后依然是 k+1是i的下一个位置的回退位置

		这里我们之所以可以抽象的理解k前面直到0都与i前面 直到i前面直到 i-k都是完美匹配的也是得益于逻辑主串与逻辑目标串的再匹配k = next[k]机制 ;
		当szDest[k] != szDest[i]不匹配时,可以回退至 next[k]位置继续对逻辑主串将要匹配位置 i 与逻辑目标串将要匹配位置k=next[k]位置做匹配
		如果匹配了 则 k+1是i的下一个位置的回退位置 否则继续循环
		这里难于理解的点就是循环的代码实际做了递归的事情

		当k=-1时就说明i的下一个位置不可能出现公共最大前后缀,此时i的下一个位置的回退位置就是0
		*/
	while (i<strlen(szDest)-1)//这里i确定的是i的下一个位置的回退位置
	{
		if (k == -1 || szDest[k] == szDest[i])//求i的下一个位置的最大匹配长度
		{
			++i;
			++k;
			next[i] = k;
		}
		else
			k = next[k];
	} 
	return next;
}

int kmp(const char* szSrc, const char* szDest)
{
	int nSrcSize = strlen(szSrc);
	int nDestSize = strlen(szDest);

	int iSrc = 0;//主串将要匹配位置索引
	int iDest = 0;//目标串将要匹配位置索引

	//获取next表
	int* next = new int[nDestSize];
	getNext(szDest, next);

	while (iSrc<nSrcSize && iDest<nDestSize )//匹配只至超出主串或者目标串范围
	{
		if ( iDest == -1 ||szSrc[iSrc] == szDest[iDest])//
		{
			++iSrc;//主串将要匹配位置索引只能是正常成功匹配驱动,或者由-1(next[0])驱动,即主串当前匹配位置无法匹配目标串0位置,需要后移主串匹配位置
			++iDest;//目标串将要匹配位置索引正常成功匹配驱动
		}
		else
			iDest = next[iDest];//若匹配失败则由next表驱动
	}
	if (iDest >= nDestSize)
		return iSrc - nDestSize;//返回主串上第一个匹配目标串的位置,不用考虑越界 iSrc的增量一定大于等于iDest的增量
	return -1;//失败返回无效索引
}


int main()
{
	int match=kmp("abababbabaaaabbbaabababaaaaababbab", "aaab");
	return 0;
}

参考:KMP算法详细讲解(看完不会请打我)

你可能感兴趣的:(算法,c++,图论,KMP)