详解KMP算法

注:最近刷题刷到KMP算法,大致印象还有但是细节有些遗忘了,因此特地回顾了大一下学期数据结构所学的内容,在此进行一下记录。

 

一、模式匹配

KMP算法解决的的问题是模式匹配,也就是子串定位,是串处理中最重要的运算之一,具体任务描述如下:

假设有一个主串S=“a1a2...an”, 子串T=“b1b2...bm”(m<

 

二、模式匹配的经典算法

记主串为S,子串为T。从i=1, j=1开始匹配,若匹配成功(S[i]==T[j]),则i,j分别向后移动一位;若匹配失败,j退回1,i退回i-j+2,再重新开始匹配,如下图所示:

详解KMP算法_第1张图片

 

具体算法如下:

int Index(String S, String T, int pos)
{
    int i=pos, j=1;
    while(i<=S[0]&&j<=T[0]){
        if(S[i]==T[j]) i++,j++;
        else i=i-j+2, j=1;    //指针回溯
    }
    if(j>T[0]) return i-T[0];    //匹配成功
    return 0;    //匹配失败

}

因此最好的时间复杂度为O(m+n),最坏的时间复杂度为O(m*n).

 

三、KMP

经过观察发现当每次匹配失败之后,不需要将指针i回溯,也不需要一个一个地移动j指针,因为字符串存在一些特征,当前位置匹配失败之后如果按照最传统的方法直接后移一位的话,下一步匹配还是会失败,造成了计算资源的浪费,因此提出了KMP算法。其中指针i始终是往前走的,此外还需要保留一个next数组来判断在当前位置j匹配失败后应该移动到的位置next[j]。大致的过程如下图所示:

详解KMP算法_第2张图片

1、具体算法如下:

int Index_KMP(String S, String T, int pos){
    int i=pos, j=1;
    while(iT[0]) return i-T[0];    //匹配成功
    else 
        return 0;
}

2、子串next数组的求解

next数组的求解主要是与子串T[1...j-1]前后缀相同子串的长度有关,如果next[j]=k,则T[1..k-1] == T[j-k+1..j-1]T[1..k-1] == T[j-k+1],因此得到如下求解公式:

详解KMP算法_第3张图片

详解KMP算法_第4张图片

具体代码如下:

void get_next(String T, int next[])
{
    j=1, next[1]=0, k=0;
    while(j

 

 

3、进一步改进

可能会存在这样的情况,T[j]=T[next[j]],这样如果是在T[j]处失配移动到T[next[j]]处依然会失配,因此提出如下改进方案:

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

 

 

 

你可能感兴趣的:(详解KMP算法)