KMP算法

KMP算法

假设现在我们面临这样一个问题:给两个字符串s、p,请问p是否是s的子串?这就需要定位子串,也既串匹配。串匹配运算的应用非常广泛,在搜索引擎、拼写检查、语言翻译、数据压缩等应用中,都需要进行串匹配。KMP算法就是串的一种匹配算法。

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

KMP算法的匹配流程

“假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置 如果j = -1,或者当前字符匹配成功(即S[i] ==
P[j]),都令i++,j++,继续匹配下一个字符; 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i
不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j -
next[j],且此值大于等于1。”

现在我们用一个问题介绍一下。假设主串为“s1s2…sn”,模式串为“t1t2…tm”,为实现KMP算法,需要解决下述问题:当匹配过程中产生“失佩”(si≠tj)时,模式串“向右滑动”的可行的距离多远?
假设此时应与模式中第k(k<j)个字符比较,则模式中前k-1个字符的子串必须满足下列关系式,且不可能存在k’>k满足下列关系式①
“t1t2…tk-1”=“si-k+1si-k+2…si-1”

而已经得到的“部分区配”的结果是②
“tj-k+1tj-k+2…tj-1”=“si-k+1si-k+2…si-1”

由①②推得下列等式③
“t1t2…tk-1”=“tj-k+1tj-k+2…tj-1”

反之,若模式串中存在满足③的两个子串,则当匹配过程中,主串中第i个字符与模式串第j个字符比较不等时,仅需将模式向右滑动至模式中第k个字符和主串中第i个字符对齐,此时,模式中头k-1个字符的字串“t1t2…tk-1”必定与主串中第i个字符之前长度为k-1的子串“si-k+1si-k+2…si-1”相等,由此,匹配仅需从模式中第k个字符与主串中第i个字符开始,依次向后进行比较。

若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。由此可引出模式串中next函数的定义:

           0     j=1(t1与si比较不等时,下一步进行t1与si+1的比较)

next[j]= Max{k l1<k<j且有“t1t2…tk-1”=“tj-k+1tj-k+2…tj-1”}
1 k=1(不存在相同子串,下一步进行t1与si的比较)

算法描述

int  Index_KMP(SString  S,SString  T,int pos)
{//利用模式串T的next函数求T在主串中第pos个字符之后的位置
//其中,T非空,1<=pos<=S.length
  i=pos;j=1;
  while(i<=S.length && j<=T.length)           //两个串均未比较到串尾
     {
      if(j==0||S.ch[i]==T.ch[i])  {++i;++j;}      // 继续比较后继字符
      else j=next[j];                                      //模式串向右移动
      }
      if(j>T.length) return  i-T.length;           //匹配成功
      else  return 0;                                    //匹配失败
  }

KMP算法是在已知模式串的next函数值的基础上执行的,那么,如何求得模式串中next函数值?

1.j的前面的k个字符与开头的k个字符进行比较,若对应相等且T[k]==T[j]但T[k]!=T[0],则next[j]=0; j的前面的k个字符与开头的k个字符进行比较,若对应相等且T[k]==T[j]但T[k]==T[0],则next[j]=-1; j的前面的k个字符与开头的k个字符进行比较,若对应相等且T[k]!=T[j],则next[j]=k;

2.j的前面的k个字符与开头的k个字符进行比较,若不等且T[k]==T[0],则next[j]=-1;j的前面的k个字符与开头的k个字符进行比较,若不等且T[k]!=T[0],则next[j]=0。

算法描述

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; }
    eise  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这三次比较。实际上,因为模式中的1~3个字符和第4个字符都相等,因此不需要再和主串中第4个字符相比较,哦,可以在模式中浆膜是连续向右滑动4个字符的位置直接进行i=5、j=1时字符比较。也即,若按上述定义得到next[j]=k,而模式中tj=tk,则当主串中字符si和tj比较不等时,不需与tk进行比较,直接与Tnext[k]比较,换而言之,此时next[j]=next[k]。由此得到next函数修正值。

算法描述

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];
     }
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值