KMP算法

写在前面 KMP真的好难懂 😭 首先默认暴力求解咱都会,现在来水水KMP的这么一个步骤。

首先,对字符串的匹配来说,BF求解时间复杂度太高,所以有三位大牛弄出了KMP算法。以下通过我的理解来说一说KMP。

假设主串的长度为M,子串的长度为N,现在要达到的目的是在主串M中找到N所在的对应的子串下标,如果没有就返回。
KMP的核心是是针对子串,防止重复回溯,提高效率,因此对应子串中有一个Next数组,数组存放的元素为,当前下标j处子串失配时,下次从子串的next[j]下标处,开始重新匹配。

这是为啥呢,举个栗子。

假设子串T为ACACA,主串S为BACACBACACA
第一步,子串开始移动,在T[0]!=S[0],那么子串往下走,比较T[0]和S[1],相等,接着走发现,直到T[5]的时候才与S[6]失配,那么这一次,需要再从头开始匹配,让T[0]与S[2]作比较么?显然不是肉眼来看,应该将字符串右移两位,将T[2]与S[3]作对比,提高了效率。

那么怎么找到上述例子的T[2]与S[3]呢?可以发现的是,之所以上面可以将子串移动2个位置,再进行匹配,是因为字符串的前缀中,有重复的两个元素,由此可知,当子串匹配到失配的元素时,如果前面有前缀后缀相同的元素,那么就应该去到最相同的元素的末端,接着和主串中失配的元素相匹配。那么如何寻找子串中相同前缀后缀,如何描述呢,以此,就引出了next数组。

next数组的X下标对应的元素就是子串的元素X之前,前缀后缀字符串相等的最大长度。(前缀指的是字符前面的不包括最后一个字符的所有字符串,后缀反之。)
ACACA这个子串的next数组,可以通过数得到。由于字符串下标从0开始,所以next[0]被赋值为-1;当子串下标为1时,此时’C‘的前缀后缀没有相等元素,所以为0;子串下标为2时,’A‘的前缀后缀没有相等元素,所以为0;子串下标为3时,’C‘的前缀后缀分别为“A,A”,“AC,CA”,所以值为1,子串下标为4时,’A‘的前缀后缀分别为“A,C”,“AC,AC”,“ACA,CAC”,所以值为2,那么next数组的值为 -1,0,0,1,2。

那么对于Next数组的求解过程中,重要的是前缀所在的位置,因为前缀是相对固定的,而后缀则是一直在移动。


先贴代码,明天解释和排版,今天先sleep🎠

int*  KMP_next(string T,int *next)
{
	int next1[255] = { 0 };
	int i = -1;//前缀
	int j = 0;//后缀
	next[0] = -1;

	while (j<T.length()-1)
	{
		if (i==-1||T[i] == T[j])
		{
			i++;
			j++;
			//next1[j] = i;
			if (T[i] == T[j])
			{
				next[j] =next[ i];
			}
			else
			{
				next[j] = i;
			}


		}
		else
		{
			i = next[i];
		}
	}

	return next;
}
int KMP(string S,string TT)
{
	int next[255] = {0};
	KMP_next(TT,next);
	int pos = 0;
	int T_pos =0;
	int slen = S.length();
	int tlen = TT.length();
	while ( pos< slen && T_pos< tlen)
	{
		if (T_pos==-1||S[pos]== TT[T_pos])
		{
			pos++;
			T_pos++;
		}
		else
		{
			//回溯
			T_pos = next[T_pos];
		}
	}
	if (T_pos == TT.length())//找到了子串的末尾 但是还没找到相同的串
	{
		cout << "在字符串的" << pos - TT.length() << "位置处找到了" << endl;
		return pos - TT.length();

	}
	else
	{
		cout << "没有匹配的字符串" << endl;
		return -1;
	}


}
void test15()
{
	cout << "请输入主串" << endl;
	string S;
	getline(cin,S);
	cout << "请输入目标串" << endl;
	string S1;
	getline(cin, S1);
	KMP(S, S1);


}
int main()
{
	cout << "正在测试我的代码。。" << endl;
	srand((unsigned int)time(NULL));
	test15();
	system("pause");
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值