算法数据结构和算法分析

从零开始养成算法·篇十:初探KMP

2020-04-24  本文已影响0人  文竹_自然

啥是KMP算法

KMP 算法是 D.E.Knuth、J,H,Morris 和 V.R.Pratt 三位神人共同提出的,称之为 Knuth-Morria-Pratt 算法,简称 KMP 算法。该算法相对于 Brute-Force(暴力)算法有比较大的改进,主要是消除了主串指针的回溯,从而使算法效率有了某种程度的提高。

KMP在字符串匹配上的应用

设|S| = n , |T| = m。首先考虑一个朴素算法,那就是将字符串 S 中的每一个长度为m的子串都与 T 进行一次匹配,失配后再匹配下一个,复杂度O(NM)。

手动模拟一下可以发现,上述做法中指向字符串 S 的指针和 T 的指针都有回退 ,但实际上我们并不需要发生回退,KMP算法就是通过防止指针回退来提升朴素算法效率的。

假设我们 S[i] 和 T[j+1] 发生了失配,如果我们知道 “T 中以 j 为末尾的真子串” 和 T[1, j] 的最长公共前缀的长度(假设为len,len一定小于 j ),那么显然 T[1, len] = S[i-len+1, i];于是此时的 j = len,接着匹配即可。我们用nex数组(见下文)来存放 T 对应位置的“len”。

\color{red}{详细的讲,KMP算法分为两步:}

1.对字符串 T 进行自我“匹配”,求出一个数组 next;

2.对字符串 T 与 S 进行匹配,求出一个位置。

\color{red}{指针回退:}在朴素做法中,如果发生失配,则要将指向 S 串的指针回退到当前子串起始位置,并右移至下一个子串起始位置,同理指向 T 的指针也要回到起始位置。

\color{red}{第一步:next数组:}

1.默认next[1] = 0,代表从0开始遍历;
2.设计两个索引下标i=0,j=1,i用来求解回溯的地址,j用来模式串的遍历;
3.如果i=0,表示此时模式串没有出现相同字符,此时回溯地址为1即next[j] = 1,表示需要从模式串的第一个字符开始比较;
4.如果T[i] == T[j]表示模式串中出现了重复字符,则next[j] = i;
5.如果3和4都没有,表示从[i,j]这个范围没有出现回溯地址的变化,把i回溯到next[i]的位置。

\color{red}{代码:}

void get_next(String T,int *next){
   int i,j;
   j = 1;
   i = 0;
   next[1] = 0;
   while (j < T[0]) {
       //printf("i = %d j = %d\n",i,j);
       if(i ==0 || T[i] == T[j]){
           ++i;
           ++j;
           next[j] = i;
       }else
       {
           i = next[i];
       }
   }
}

\color{red}{tips:}上诉方法在模式串存在字符重复的情况下,会有浪费对比的流程,可以考虑优化如下:

void get_nextVal(String T,int *nextVal){
   int i,j;
   j = 1;
   i = 0;
   nextVal[1] = 0;
   while (j < T[0]) {
       if (i == 0 || T[i] == T[j]) {
           ++j;
           ++i;
           if(T[i] != T[j])
               nextVal[j] = i;
           else
               nextVal[j] = nextVal[i];
       }else{
           i = nextVal[i];
       }
   }
}

\color{red}{第二步:匹配算法:}

int Index_KMP(String S,String T,int pos){
   
   int i = pos;
   int j = 1;
   int next[MAXSIZE];
   
   get_next(T, next);
   count = 0;
   while (i <= S[0] && j <= T[0]) {
       
       if(j == 0 || S[i] == T[j]){
           i++;
           j++;
       }else{
           j = next[j];
       }
   }
   
   if (j > T[0]) {
       return i-T[0];
   }else{
       return -1;
   }
   
}
上一篇下一篇

猜你喜欢

热点阅读