KMP算法笔记

参考视频
慕课数据结构——KMP算法

完整代码

int KMP(string m,string p,int next[])
{
    int i=0,j=0;
	while(i<m.length()&&j<p.length())
	{
		if(j==0||m[i]==p[j])
		{
			i++;         //继续对下一个字符比较 
			j++;         //模式串向右滑动 
		}
		else j=next[j];
	}
	if(p[j]=='\0') return (i-j+1);    //匹配成功返回下标 
	else return -1;                 //匹配失败返回-1 
}

KMP算法的基础过程

主要过程

模式串的next值的求解思想

1)主串指针i不回溯
因为当主串匹配到i失配时,i前面的字符和模式串j前面的字符都是相等的,现在我们要把模式串往后边尽可能多地挪一挪,假设挪动k位,使得i前面的已经匹配好的部分串能够得到最大的利用。要知道,i该从哪个j开始继续匹配当然:为了防止漏掉,1<k<j

2)将模式串移动k位后,即j=next[j]。

3)模式串的next值与主串S无关而只与模式串T有关:因为当主串匹配到i失配时,i前面的字符和模式串j前面的字符都是相等的。本质上是在对模式串本身做研究,故与主串无关。

5)具体应该移动到哪里?
模式串的T[j]与主串的S[i]失配了,S[i]!=T[j],模式串向后移动k位后,模式串的T1到Tk-1与主串的Si-k+1到Si-1相等,因为当主串匹配到i失配时,i前面的字符和模式串j前面的字符都是相等的。故Si-k+1到Si-1等于模式链中的Tj-k+1到Tj-1。
T1到Tk-1是T1到Tj-1的真前缀串
而Tj-k+1到Tj-1是T1到Tj-1的真后缀串
所以实际上是找T1到Tj-1这个串中的真前缀串和真后缀串相等的时候,选取最大的那个子串(后缀或者前缀都是串的子串)的长度再加上1就是新的j。
i到6的时候匹配失败
Si=6的时候匹配失败,这个时候Si应该与哪个Tj进行匹配才能保住前面已经匹配好的部分呢?Si=6和第一个e比较
j前面的串a b c d e e a b c d ,长度相等的最大真前缀和真后缀是a b c d。长度为4,那就让j=5,(i,j从1开始)即Si=6和Tj=e比较。
后缀和前缀相等,就可以在失配时利用之。

求next值的具体算法

 int next[10];
 char p[10];
 int j=1,k=0;
 next[1]=0;
 while(j<10)
 {
   if(k==0||p[j]==p[k])
   {
      j++;
      k++;
      next[j]=k;
    }
    else
      k=next[k];
  }

next数组的求法是关键。
next数组存放的是模式串中,每个字符失配时,模式串该跳去的那个id,以达到利用最大公共前后缀的目的。应该求什么?

如图: 在p[10]=d时失配,根据对d之前的部分串的最大公共前后缀研究得知,模式串该回到p[3]去,所以,next[10]=3.

具体推导:
 j有三个去处1)j++2)j=03)j=next[j]
在next数组的求解过程中:
初始条件:
k留在前缀,j留在后缀。
k=0,j=1;
next[1]=0,//意为在第一个字符就失配,主串得往后面走.

if(k==0||p[j]==p[k])
   {
      j++;
      k++;
      next[j]=k;
    }

执行这个if结构有两种情况
1)k==0
j之前的部分串前后缀失配导致k=0,因而理所当然地j++,k++,进行下一步判断,同时next[j]=0
2)p[j]==p[k]
说明p[k]和p[j]匹配成功了,那么假如在p[j+1]失配,模式串就该回到k+1去。也是j++,k++.

else
	k=next[k];

执行这个else分支
说明k既不等于0,p[j]也不等于p[k],已经匹配了一段,到此为止失配,则k应该回到?
这里的行为类似主框架中j=next[j]的行为。虽然这里失配了,难到k就一定要回到0,重新开始吗?不是的,因为这里失配之前的已经匹配的部分可以利用。
这里又相当于把前缀当模式串,后缀当主串进行匹配。故k=next[k]

KMP算法是一种字符匹配算法,用于在一个文本S内查找一个模式P的出现位置。它的时间复杂度为O(n+m),其n为文本的长度,m为模式的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本为S="ababababca",模式为P="abababca",我们想要在S查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

维何M762

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值