KMP模式匹配字符串

本文深入探讨了KMP算法的核心思想与优化技巧,重点讲解了如何通过Next数组高效匹配主串与模式串。通过实例分析Next数组的求解过程,提供了一种改进的求法,使算法更加灵活。同时,阐述了KMP算法在字符串匹配中的实际应用,包括匹配操作的实现与性能优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
*
*   KMP算法的学习【复杂度O(M + N)】 两个字符串的长度和
*   Next数组的求法【核心】 + 思想: 指示主串的指针[i]不回溯,只是指示模式串的指针[j]发生回溯
*	
*	例子:
*	主串S: ababcabcaabcbaabc
* 模式串T: ababcabababc
*
* 模式串对应的Next数组的值:
*
  未改进前的Next值:
* Next值: 0 1 1 2 3 1 2 3 4 5 4 5
*         a b a b c a b a b a b c


//有一种情况: 这种情况的话,那个得到Next值的方法就不好了...
//
//  主串S:aaabaaabaaabaaabaaaab
//模式串T:aaaab
//Next值: 01234  

  改进后的Next值:
* Next值: 0 1 0 1 3 0 1 0 1 5 1 3		//其实只是 比如第三个a的头上是1,那去找下T[1]是否等于a,如果等于a,那么就把自己的Next值和它相等.即0,其他的也如此!
*         a b a b c a b a b a b c


* Next值的求法:【数组的第零个舍去】  
	需要一个求模式串Next数组的函数【计算机求Next值的过程是一个递推的过程】

	(1)函数算Next值的分析:
	已知: Next[1] = 0;
	假设: Next[j] = k; 又有T[j] == T[k]
	则	  Next[j+1] = k+1;	//Next[j] + 1;
	若	  T[j] != T[k]
	则需要往前回溯,检查T[j] 是否和 T[Next[k]]相等,一直找到相等的字符为止,然后 Next[j] 就等于 Next[相等的这个字符] + 1;
	算法代码实现: GetNext(char * ModelString, int & Next[]);


*   (2)手动算Next[j]: [比较好理解Next数组的算法]
	
*   当j == 1时, Next[1] = 0;
*	当j前面有匹配的,取(最大的匹配字串的字符个数 + 1)作为Next[j]的值  (重: 是从当前j的位置往前面找相等的子串)
*   当除了上面两种情况外,其他的情况Next[j] = 1;
	例:
	a b a b c a b a b a b c
	0 
	j == 1; 既字符串的第一个位置: 第一个a, Next[1] = 0;
	j == 2; 字符串的第二个位置: 字符b, 分析,第二个字符前面只有一个字符a,所以没有匹配的重复字符子串,既 Next[2] = 1;
	j == 3; 字符串的第三个位置: 字符a, 分析,第三个字符前面有两个字符 a b,a 和 b不相等,所以也是 Next[3] = 1;
	j == 4; 字符串的第四个位置: 字符b, 分析,第四个字符前面有三个字符 a b a, 因为a 和 a 相等,所以 Next[4] = 1+1 = 2;
	j == 5; 字符串的第五个位置: 字符c, 分析,第五个字符前面有四个字符 a b a b ,因为 ab 和 ab子串[ab长度为2]相等,所以 Next[5] = 2 + 1 = 3;
	j == 6; 字符串的第六个位置: 字符a, 分析,第六个字符前面有五个字符 a b a b c,因为c和前面的子串中没有相等的,b c和前面也没有相等的,所以 Next[6] = 1;
	.....
	.....依次类推
*
*
	利用Kmp和Next数组的运算,就是当 S[i] == T[j] 的时候 ++i, ++j;
	当 S[i] != T[j] 的时候,回溯j,不能动i, j = Next[j];	//等于当前Next[j]的值
	当 j == 0;//表示回溯到了第一个位置了,这个时候要把 i向后移动一位,而j = 0(默认不用这个数组的下标0的元素),所以也要++j, 再比较S[i] ? T[j] 
*/
#include <stdio.h>
#include <string.h>

//为改进前的求Next函数
void GetNext(char * ModelString, int * next)
{	//求模式串ModelString的next值,并存入next数组中
	int i = 1;
	int j = 0;
	next[1] = 0;	//这个是已知的【默认都是这样】
	int len = strlen(ModelString);
	while (i < len)
	{
		if (j == 0 || ModelString[i] == ModelString[j])
		{
			++j;	
			++i;
			next[i] = j;
		}
		else
			j = next[j];
	}
}
//改进后的求Next函数
void GetGoodNext(char * ModelString, int * next)
{
	int i = 1;
	int j = 0;
	next[1] = 0;	//这个是已知的【默认都是这样】
	int len = strlen(ModelString);
	while (i < len)
	{
		if (j == 0 || ModelString[i] == ModelString[j])
		{
			++j;	
			++i;
			if (ModelString[i] != ModelString[j])
              next[i] = j;
			else 
              next[i] = next[j];

		}
		else
			j = next[j];
	}
}


int IndexKmp(char * MainString, char * ModelString, int Pos)
{	
	//  1<=Pos<=strlen(MainString)
	int i = Pos;
	int j = 1;	//从第一个位置开始
	int lenMainString = strlen(MainString);
	int lenModelString = strlen(ModelString);
	while (i<=lenMainString && j<=lenModelString)
	{
		if (MainString[i] == ModelString[j] || j == 0)	
		{
			++i;
			++j;
		}
		else
		{
			j = Next[j];		//把当前位置的Next值赋给j, Next数组可以做成全局变量,也可以作为参数传进来
		}
	}
	if(j > lenModelString)
	{	//匹配成功了,返回匹配字符串的第一个字符
		return i - lenModelString;	
	}
	else
	{	//匹配不成功,返回0
		return 0;	
	}
}
int main()
{

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值