KMP算法以及next函数值、nextval函数值的计算方法

KMP算法以及next函数值、nextval函数值的计算方法

数据结构中串涉及的内容即串的模式匹配,比较难理解的KMP算法,难在next函数值和nextval函数值的求解
一、问题描述
给定一个主串S及一个模式串P,判断模式串是否为主串的子串;若是,返回匹配的第一个元素的位置(序号从1开始),否则返回0;

先了解一下最简单直观的模式匹配算法BF(Brute-Force)算法,对理解KMP算法做个铺垫!

1、BF算法
假设S=“BBC ABCDAB ABCDABCDBADE”,P=“ABCDABD”。

  • 分别利用计数指针i和j指示主串S和模式串P中当前正在比较的字符位置,i的值可以指定,j初值为1;
  • S.ch[i]和P.ch[j]比较,若相等,则i和j分别指示串的下个位置,继续比较后续字符;
  • 若不相等,指针后退重新开始匹配,从主串的下一个字符(i=i-j+2)起再新和模式串的第一个字符(j=1)比较。

1、当S=“BBC ABCDAB ABCDABCDBADE”
与P="ABCDABD"进行匹配时:

  串S   BBC ABCDAB ABCDABCDBADE
  串P   ABCDABD

因为B与A不匹配,模式串后移一位。

  1. 直到文本串有一个字符与模式串中的第一个字符相同为止,然后逐一比较下一个字符,发现“空格”与“D”不匹配。

     BBC ABCDAB ABCDABCDBADE
         ABCDABD
    

3.重复第1第2步,能找到文本串中有“ABCDABD”与模式串相匹配,但效率很慢。
下面我们来了解另一种改进的模式匹配算法。

2、KMP算法
KMP算法,其改进在于,每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。

KMP算法是在已知模式串的next函数值的基础上执行的,那么,如何求得模式串的next函数值呢?(模式串根据next函数值向右“滑动”距离此函数值仅取决于模式串本身)

3、计算next函数值
【举例】
串“ababaaababaa”的next数组为

  • next[1]=0
  • next[2]=1

(这两个值是约定的)

  • next[3]:"ab"没有相同的前缀或后缀,所以模式串又得从头开始匹配,因此next[3]=1;
  • next[4]:“aba"的最长公共串是"a”,前三位匹配上了,并且第一位和第三位相同,因此可以将模式串整体向右移动,直到将原来的第一位移到原来的第三位上,继续进行匹配,而原来模式串指针指着的第四位现在指向第二位了,因此next[4]=2;
  • next[5]:“abab"的最长公共串是"ab”,用同样的方法得出next[5]=2;

以此类推······

  • next数组为:

0 1 1 2 3 4 2 2 3 4 5 6

4、计算nextval函数值
【举例】
串“ababaabab”的nextval为
在这里插入图片描述
总结如下:
1、nextval[1]和next[1]的值一样,为0;
2、如果s[i]==s[j],则nextval[i]=nextval[j];反之,nextval[i]=next[i].

代码附上
通过递推求得next数组

Void  GetNext (char *p, int next[ ])
{
Int plen=strlen(p);
Next [0]=-1;
Int k=-1;
Int  j =0;
While(j<plen-1);
{
     If(k==-1||p[j]==p[k])
{
           ++k;
           ++j;
           Next[j]=k;
}
Else
{
           K=next[k];
}
}
}

KMP算法:

Int KmpSearch(char *s,char *p)
{
     Int i=0;
     Int j=0;
     Int slen = strlen(s);
     Int plen = strlen(p);
     While (i<slen && j<plen)
     {
         //如果j=-1,或者当前字符匹配成功,都令i++,j++
         If (j==-1 || s[i]==p[j])
         {
              I++;
              J++;
          }
          Else
          {
              //如果j!=-1,且当前匹配失配,则令i不变,j=next[j]
              //next[j]即为j所对应的next值
              J=next[j];
           }
      }
      If (j==plen)
           Return  i-j;
      Else
            Return -1;
}

此时,我们得到修改后的求next数组的代码

Void GetNext (char *p, int next[ ])
{
Int plen = strlen(p);
Next [0]=-1;
Int k=-1;
Int j =0;
While(j<plen-1);
{
   //p[k]表示前缀,p[j]表示后缀
     If (k==-1 || p[j]==p[k])
{
           ++j;
           ++k;
           If(p[j]!=p[k])
              Next[j]=k;
           Else
              //因为不能出现p[j]=p[next[j]]所以当出现需要递 //推时,k=next[k]=next[next[k]]
              Next[j]=next[k];
}
Else
{
           K=next[k];
}
}
}

你可能感兴趣的:(KMP算法以及next函数值、nextval函数值的计算方法)