计算机基础数据结构讲解第十二篇-字符串模式匹配KMP算法第二讲

  现在继续讲解KMP算法的有关知识,以及KMP算法的代码和改进,改进用nextval[]数组。

一:next公式讲解

  由公式可知next[1]=0,也就是模式串第一个字符(j=1),与主串第i个字符发生失配时,规定next[1]=0,将模式串右移一位,然后模式串第一个字符和主串的下一个位置(i+1)进行比较。
  设next[j]=k,那此时next[j+1]等于多少呢?可能有两种情况:

1.Pk=Pj

  若Pk=Pj,则说明有最大公共前后缀,此时next[j+1]=k+1,即next[j+1]=next[j]+1。

2.Pk≠Pj

  若Pk≠Pj,则说明找到的最大公共前后缀不符合,这时可以把求next函数值的问题视为一个模式匹配的问题,如果不匹配,需要找到长度更短的相等前后缀,依次类推则一直向后寻找,寻找更小的k为k',则next[j+1]=k'+1。也可能不存在任何k'满足上诉条件,即不存在长度更短的相等前缀后缀,令next[j+1]=1。
  关于知道next[j]的值,计算next[j+1]的值这里已经介绍了,下面就来介绍求next值的程序。

二:求next程序

  算法如下:

void get_next(String T,int next[]){
  int i=1;j=0;
  next[1] = 0;
  while(i<T.length){
    if(j==0!!T.ch[i]=T.ch[j]){
      ++i;++j;
      next[i] = j;  //Pi=Pj
    }
    else
      j=next[j];  //否则令j=next[j],循环继续
  }
}

  用手工的方法计算的时候,比较困难,所以还是用之前的方法求next数组。而KMP算法就比较简单了,根据之前得到的过程代码如下:

int Index_KMP(String S,String T,int next[]){
  int i = 1;j = 1;
  while(i<=S.length&&j<=T.length){
    if(j==0||S.ch[i]=S.ch[j]){
      ++i;++j //没有公共前后缀或子串第一个字符比较,直接比较i+1位
    }
    else
      j=next[j];  //模式串向右移动
  }
  if(j>T.length)
    return i = T.length;  //匹配成功
  else
    return 0;
  }

  由上面的推断可知,KMP算法的时间复杂度是O(n+m),但在一般情况下,普通模式匹配的实际执行时间近似为O(n+m),因此至今仍被采用。KMP算法仅在主串与子串有很多部分匹配,即最长公共前后缀时才显得比普通算法快得多,其主要优点是主串不回溯。

三:KMP算法的进一步优化

  前面定义的next数组在某些情况下尚有缺陷,还可以进一步优化。当Pj≠Sj,下次匹配应该是P(next[j])跟Sj比较,如果Pj=P(next[j]),那么相当于拿一个和Pj相等的字符跟Sj比较,这必将导致继续失配,这样的比较毫无意义。
  那么如何进行处理呢?就可以继续递归,将next[j]修正为next[next[j]],直至两者不相等为止,更新后的数组命名为nextval。计算next数组修正值的算法如下,匹配算法不变,只需要修改一部分。代码如下:

void get_nextval(String T,int nextval[]){
  int i=1;j=0;
  nextval[1] = 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=next[j];
  }
}
posted @ 2020-09-26 23:38  一只帅气的IT小昂  阅读(313)  评论(0编辑  收藏  举报