KMP算法及其优化——串模式匹配算法

文章目录

  • KMP(Knuth Morris Pratt)算法
    • next函数
    • KMP具体代码
    • next函数改进

KMP(Knuth Morris Pratt)算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

模式匹配的改进算法——KMP算法是在O(m+n)的数量级上完成串的模式匹配。算法的改进之处在于:每当1趟匹配过程中出现字符比较不等时,指向主串的指针i不会溯,而是利用已经得到的“部分匹配”的结果将模式串向右滑动尽可能远的一段距离后继续进行比较。

例:
主 串 : S=a c a c b a c b a a b c a
模式串:T=a c b a b
KMP算法及其优化——串模式匹配算法_第1张图片
如图所示,在匹配过程中,第1次比较不成功时,i=3,j=3,此时i指针不变,仅需将模式串T向右移动2个字符的位置,继续进行i=3,j=1的下一趟比较;第2趟匹配中,前4个字符比较成功,但i=7,j=5时比较失败,此时将模式串向右移动3个字符的位置,继续进行i=7,j=2的下一趟比较,直至比较成功。在整个匹配过程中,i指针不回溯

可是我们怎么知道每趟匹配移动的位置呢?

为此,我们定义一个数组从而记录每趟模式串移动的位置next[ j ]函数,表明当模式中第j个字符与主串中相应字符“失配”时,在模式串中需重新和主串中该字符进行比较的字符的位置。

next函数

  • next[ j ]函数定义如图:
    KMP算法及其优化——串模式匹配算法_第2张图片

下面就next函数进行介绍:

  • max意为最大值,即模式串匹配失败字符前的所有字符,所有能满足头部与尾部向同的最大长度。(此处最难理解
    下面进行解释:
    KMP算法及其优化——串模式匹配算法_第3张图片
    上图中,max=k,而条件是(k-1)个元素,意为若存在前三个与后三个相同则k-1=3;k=4;此时next值=max=k=4;

    例1:如图一(本文章第一张图片)所示在第三趟匹配中是从j=2开始匹配,即next值是2,那么反过来max=2;k-1=1;意思是模式串中匹配失败的字符前只有最前面的一个字符与最后面的一个字符相同,我们看图。
    在这里插入图片描述
    只有首字符a与末尾字符a相同;前两个字符a c与后两个字符b a不同;前三个字符a c b与后三个字符c b a不同;所以只有前1个与后1个符合,那么k-1=1;k=2;next值为2。

    例2:如图:在这里插入图片描述
    红色字符意为与主串匹配失败的,则之前有6个字符,那么需要比较五次了;
    KMP算法及其优化——串模式匹配算法_第4张图片
    由图我们可知,第一次和第三次符合,那么我们取最大值,即k-1=3;next值 = max = k = 4;
    是不是感觉一个一个比较比较麻烦,不要怕,我们可以直接通过代码实现,从而计算出所有的next值!代码在下面

  • 当 j =1时,next为0;

  • 其他情况即j不是1时,也不存在当前匹配失败时的字符(如上图匹配中红色字符)前没有头部与尾部相同的情况。此时next值为1
    在这里插入图片描述
    模式串中b字符为红色代表示此时与主串不符,并且红色字符b之前头部与尾部不同,即a与c不同为其他情况。因此,next值为1,要从j=1开始匹配。

代码如下:

void get_next(SString T,int next[])
{//求模式串T的next函数值并存入数组next 
	i = 1;next[1]=0;j=0;
	while(i<T.length)
	{
		if(j==0||T.ch[i]==T.ch[j]){
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];
	}
} 

对于代码中 j = next[ j ];搞了半天还是不太懂,现在才大一学的还不多,等到开学问下老师,回来更新哈~

KMP具体代码

int Index_KMP(SString S,SString T,int next[]){
	i = i , j=1;
	while(i<=S.length && j<=T.length){  //两个串均为比较到串尾 
		if(j==0||S.ch[i]==T.ch[j]){    //继续比较后继字符 
			++i;
			++j;
		}
		else                           //模式串向右移动 
			j=next[j];
	}
	if(j>T.length)                     //匹配成功 
		return i-T.length;
	else                               //匹配失败 
		return 0;	
}

next函数改进

前面定义的next函数在某些情况下还有缺陷。例如模式“aaaab”在主串“aaabaaaab”匹配时,当i=4、j=4时,S.ch[4]≠T.ch[4],由next[j]的指示还需进行i=4、j=3,i=4、j=2,i=4、j=1这3次比较。实际上,因为模式中第1~3个字符和第4个字符都相等,因此不需要再和主串中第4个字符相比较吗,而可以将模式连续向右滑动4个字符的位置直接进行i=5、j=1时的字符比较。
在这里插入图片描述
下面为next函数修正值:
KMP算法及其优化——串模式匹配算法_第5张图片
代码如下:

void get_nextval(SString T,int nextval[])
{//求模式串T的next函数修正值并存入数组nextval 
	i =1;nextval[1]=0;j=0;
	while(i<T.length)
	{
		if(j==0||T.ch[i]==T.ch[j]){
			++i;++j;
			if(T.ch[i] !=T.ch{j})
				nextval[i] = j;
			else
				nextval[i]=nextval[j];	
		}
		else
			j = nextval[j];
	}
}

你可能感兴趣的:(数据结构)