详解kmp算法

了解kmp算法之前先要知道为什么要有kmp算法

1.朴素模式匹配

        1.1基本思想:

从主串test 和模式串pattern 的第一个字符开始,将两字符串的字符一一比对,如果出现某个字符不匹配,主串回溯到第二个字符,子串回溯到第一个字符再进行一一比对。如果出现某个字符不匹配,主串回溯到第三个字符,子串回溯到第一个字符再进行一一比对…循环一直到子串字符全部匹配成功。

        1.2例如:

第一次:不匹配

第二次:主串从第2个字符,模式串从头开始,直到b与c不匹配

 第三次:主串从第3个位置开始,模式串从头开始,a与c不匹配 

第四次:主串从第4个位置开始,模式串从头开始,匹配成功

 朴素模式匹配的缺点:

当主串与模式串能部分匹配时, 主串的扫描指针i经常回溯,导致时间开销增加。

2.kmp算法

         2.1基本思想

当字串与模式串不匹配的时候,主指针不需要再次回溯,只需要调整模式串的位置

例如:

当扫描到 a , f 不匹配时,如果是朴素模式匹配那么 i指针会退到第二个位置,j指针会退到开头位置

 像这个样子:

而如果是kmp算法,会调整 j 的位置,而不使 i 指针回退

 

 实现这个功能那就必需要一个next数组

        2.2next数组

kmp算法最主要的就是next数组

next数组作用

  • next[i]的值表示下标为i的字符前的字符串最长相等前后缀的长度

  • 表示该处字符不匹配时应该回溯到的字符的下标

        2.3next数组的实现

(1)初始化 i ,j,next[0]。i 指针指向前缀的最大长度,j 指针指向后缀的最大长度

(2)当i,j指针指的元素不相等时

(3)当i,j指针指的元素相等时

(4)next数组赋值或调整 j 指针的位置

        2.4代码实现

这里初始化为next[0]=-1,j=0,k=-1,之后得到 next[i] 数组就是对应的值

如果初始化为next[1]=0,j=1,k=0,则需要初始数组空一格位置

void GetNext(char* p, int next[])
{
	int pLen = strlen(p);
	next[0] = -1;
	int k = -1;   //k指向前缀
	int j = 0;   //j指向后缀
	while (j < pLen - 1)
	{
		//p[k]表示前缀,p[j]表示后缀
		if (k == -1 || p[j] == p[k])
		{
			++k;
			++j;
			next[j] = k;
		}
		else
		{
			k = next[k];   //回退因为如果此时最大前后缀不满足,则继续看前一个元素的最大前后缀值
		}
	}
}

        

        2.5next 数组举例

逻辑上可以理解为i,j分别指向两个相同的串,从而代表前缀和后缀。实际只是在一个串操作

 

 

 

 此时i,j指的元素不相同,需要缩小最大前后缀的长度:j=next[j]

 

 

 

 

当i<length-1时退出循环,每次字符的前面匹配的字符的最大前后缀均以求出 

     

           2.6操作中j=next[j]解释

 

 

         2.7kmp算法代码实现

int KmpSearch(char* s, char* p,int next[])
{
	int i = 0;
	int j = 0;
	int sLen = strlen(s);
	int pLen = strlen(p);
	while (i < sLen && j < pLen)
	{
		//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++    
		if (j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]    
			//next[j]即为j所对应的next值      
			j = next[j];
		}
	}
	if (j == pLen)
		return i - j;
	else
		return -1;
}

测试:

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值