KMP算法真的搞死人,终于搞懂了

现总结如下:
想理解KMP算法就要先看看最原始的模式匹配

int  match( char   * string char   * pat)   // 原模式匹配函数
{
    
int i = 0, j = 0;

    
while (i < strlen(string&& j < strlen(pat))  {
        
if (sting[i] == pat[j]) {
            i
++;
            j
++;        
        }
 else {
            i 
= i - j + 1;   //对string回溯
        }

    }

    
    
if (j = strlen(pat))
        
return i - strlen(pat);
    
else
        
return 0;
}

  由于不必要的回溯增加了算法本身的时间复杂度,但完全不回溯就有可能漏了一个可能匹配成功的情况。
  比如 : string = aaabababcaaadfe 不回溯就是这样 aaabababcaaadfe
  pat = ababc //失配 ababc
  所以如果完全不回溯就会漏了这个情况 aaabababcaaadfe
  ababc //匹配成功
  为什么会漏了那个情况呢?
  其实这就是KMP研究的问题,是由于pat = ababc本身性质所决定,因为pat本身有重合的地方
  那么KMP是怎么做的呢?
  KMP是回溯pat串,而string串始终不回溯,这样的部分回溯就使得算法本身效率提高了很多,特别当string很长时
  现在我们就可以来研究一下KMP的算法
  我们想如果对于上面的例子,让j -= 2;i不变,是不是就是漏了的那种情况呢。显然这是对的,这样再继续下面的匹配函数过程,就不会有问题了。这就是KMP的算法所在。那怎么处理才知道什么时候让j减几呢?这就是KMP定义的failure function.
  令P = P0P1P...Pn-1是一个模式,则其失配函数f定义为:
  f(j) = i 为满足 i ="0" 如果i P0P1...Pi="Pj-iPj-i+1...Pj的最大整数">  f(j) = -1
  例如,对于模式pat = abcabcacab,有:
  j 0 1 2 3 4 5 6 7 8 9
  pat a b c a b c a c a b
  f(j) -1 -1 -1 0 1 2 3 -1 0 1
  呵呵,是不是有点晕了,我看到这也晕了。没关系,继续,go ahead! Life will be better!比比先前我的那个想法,就会明白一些,也就是说,如果比较到了j = 6的位置,出现了问题,那么我们完全可以从j = 3的位置开始比较,也就是pat abcabcacab回溯了3个位置,而string当前i的位往前数3个位置肯定是abc。是不是明白了一些。
  Anyhow,让我们先实现这个failure function再说

/*author: hujinpu
**2006.11.26
*
*/

void  fail( char   *  pat,  int   *  failure)  {
    
int i, j;
    
int n = strlen(pat);
    failure[
0= -1;  //定义最初情况
    for (j = 1; j < n; j++{
        i 
= failure[j - 1];
        
while ((pat[j] != pat[i + 1]) && (i >= 0))  
            i 
= failure[i];
        
if (pat[j] == pat[i + 1])
            failure[j] 
= i + 1;
        
else 
            failure[j] 
= -1;  //代表没有重合的,不能形成重合子串
    }
        
}


   现在再看定义了failure function有什么用
  当然是得到模式匹配的规则:如果出现了形如Si-j...Si-1= P0P1...Pj-1且Si!= Pj的部分匹配,那么,若j != 0, 则下一趟模式匹配时,从失配字符Si和模式串字符Pf(j-1)+1处重新开始比较;而若j = 0时,则继续比较Si+1和P0
  现在实现模式匹配算法pmatch()

/*author: hujinpu
**2006.11.26
*/

include 
< stdio.h >
#include 
< string .h >
#define  MAX_STRING_SIZE 100
#define  MAX_PAT_SIZE 100

int  pmatch( char   *   string char   *  pat)
{
    
int failure[MAX_PAT_SIZE];
    fail(pat, failure); 
//得到对于pat不同位置的f函数值
    
    
int i = 0, j = 0;
    
int lens = strlen(string);
    
int lenp = strlen(pat);
    
    
while (i < lens && j < lenp) {
        
if (string[i] == pat[j]) {
            i
++;
            j
++;
        }
 else if (j == 0//string字符串往后移
            i++;
        }
 else {
            j 
= failure[j - 1+ 1;  //使模式串字符转到Pf(j-1)+1
        }

    }

    
    
return ((j == lenp) ? (i - lenp) : -1); //判断是否成功匹配
}

  现在是不是很佩服D.E.Knuth,J.H.Morris和V.R.Pratt他们啊,其实很简单的原理,但就是他们才总结出来。
  创新才是最难的!

你可能感兴趣的:(Algorithms)