KMP算法—串的模式匹配算法

KMP算法是一种改进的字符串匹配算法,KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)

KMP算法的匹配过程:
在这里插入图片描述

KMP算法是怎样减少模式串与主串的匹配次数呢???

每当匹配过程出现字符比较不等

  • 主串的i 指针不动
  • 模式串的 j 指针定位到某个数

上面提到的模式串中 j 指针定位到某个数其实就是next值

若令 next[j]=k,则next[j] 表明当模式中第 j 个字符与主串中相应字符“失配”时,在模式中需要重新和主串中该字符进行比较的字符的位置


我们规定任何一个串,next[1]=0,举个例子求next:

已知模式串为ABABABB(如下图),在 j =5时,模式串与主串对应字符不匹配,那么求next值就需要找 j 前面的 j-1(即为4) 个字符。j 要移动的下一个位置需要满足这样的性质:最前面的k(即为2)个字符和 j 之前的最后k(即为2)个字符是一样的,且 k 必须是最大的情况。

此时FL=“AB”=FR,想要知道下一次的 j 指针对应的值只需要将 FL 与 FR 相重合(通过移动重合)即可 ,重合后此时 j 指向的值为第三个字符(A),说明next值为3。
在这里插入图片描述
用公式验证一下,next=FL串长+1=FR串长+1=3
在这里插入图片描述

通过上面的例子再来理解一下模式串next函数的定义

在这里插入图片描述
next[j] = 0的意思是主串的第 i 个字符与模式的第一个字符不等,应从主串的第 i+1 个字符起重新进行匹配


由此上面的定义我们可以推出模式串的next函数值,举个例子:
在这里插入图片描述
如果按照上面的next值进行匹配,就是下面的匹配过程:
在这里插入图片描述


KSP算法如下:

int Index_KMP(SString S,SString T,int pos)
{
   i=pos;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函数值(O(m))算法如下:

void get_next(SString T,int 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];
   }
}           

我们前面定义的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[3]、next[2]、next[1]改为0即可。nextval[j] 即为修正后的next值
在这里插入图片描述
在这里插入图片描述

计算next函数修正值算法如下:

void get_nextval(SString T,int 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];
   }
}               

整体代码:

#include <stdio.h>
#include <string.h>
int lens,lent;  //子串的长度
int next[100];

int KMP(char s[],char t[],int pos)
{
	int i=pos;int j=1;
	while(i<=lens && j<=lent)
	{
		if(j==0||s[i]==t[j])
		{
			++i;
			++j;
		}
		else
		 j=next[j];
	}
	if(j>lent) return i-lent;
	else return 0;
}
void getnext(char t[],int next[])
{
	int i=1,j=0;
	next[1]=0;   //第一个位0
	while (i<lent)
	{
		if (j==0 || t[j]==t[i])   //当j为第一个 或者 前一个字母和后一个字母相等
		{
			++i;
			++j;
			next[i]=j;   
		}
		else
		  j=next[j];   
	}
}
int main()
{
	char s[100],t[100];
	int i;
	gets(s);
	gets(t);
	lens=strlen(s);
	lent=strlen(t); 
	for (i=lens;i>0;i--)  //将主串所有元素全部后移
	{ 
	  s[i]=s[i-1];
	  
	}
	for(i=lent;i>0;i--)
	{
	  t[i]=t[i-1]; //将子串所有元素全部后移
	} 
	getnext(t,next);
	int x=KMP(s,t,1);
	printf("%d",x);
	return 0;
}

next修正后的代码:

#include<iostream>
#include<cstring>
using namespace std;
int lens,lent;
int next[100];

int KMP(char s[],char t[],int pos)
{
	int i=pos;int j=1;
	while(i<=lens && j<=lent)
	{ 
		if(j==0||s[i]==t[j])
		{
			i++;j++;
		}
		else
		j=next[j];
	}
	if(j>lent) return i-lent;
	else return 0;
}

void getnext(char t[],int next[]) //这个next是修正值哦 
{
	int i=1;int j=0;
	next[1]=0;
	while(i<lent)
	{
		if(j==0||t[j]==t[i])
		{
			i++;j++;
			if(t[j]!=t[i])
			next[i]=j;
			else
			next[i]=next[j];
		}
		else
		j=next[j];
	}
}

int main()
{
	char s[100],t[100];
	int i;
	gets(s);
	gets(t);
	lens=strlen(s);
	lent=strlen(t);
	for(i=lens;i>0;i--)
	{
		s[i]=s[i-1];
	}
	for(i=lent;i>0;i--)
	{
		t[i]=t[i-1];
	}
	getnext(t,next); //只需要用模式串就可以求出next
	int x=KMP(s,t,1);
	cout<<x<<endl; 
	for(int i=1;i<=lent;i++)
	cout<<next[i]<<" ";
	return 0;
}

求next也可以这样写:
在这里插入图片描述
求nextval也可以这样写:
在这里插入图片描述
推荐小张同学的博客,非常详细https://blog.csdn.net/l218623/article/details/104193791
另外可以看下天勤的这节视频


这个是关于BF算法的讲解BF算法—串的模式匹配算法
借鉴:《数据结构》 严蔚敏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值