一:KMP过程原理图示
图1,上方主串与下方模式串出现不匹配字符D,则如下图所示,因模式串D前方的AB与模式串的前缀AB重复,则模式串跳转到当前位置的前方最长重复前缀即AB的后一个位置C,继续比较。
图2
图3,以此类推,直到模式串全部匹配或者主串结束
(以上三个图来自阮一峰博客)
二 :KMP实现——见码如见人
#include <stdlib.h> #include <stdio.h> //双指针(查重指针tail前缀指针pre)比较公共前缀,最终生成模式串每个位置与主串某位置不匹配时的模式串跳转表, //跳转的位置是当前位置的前方所匹配的最长公共前缀的后一个位置,跳转后继续比较 void getNext(char P[],int len, int next[]) { int tail = 1, pre = 0,lastChar = 1,frontChar = 0; next[1] = 0; while(tail < len){ //若两处相等,意味着模式串中[以查重指针所在位置为尾部的子串tailstr]与[模式串某前缀prefix]相同(tailstr与prefix长度相等), //那么只要给查重指针保存这个prefix尾部的位置(即设置跳转位),在主串与模式串比较时就可以用来移动模式串了。 //移动过程是:若主串当前位置s与模式串当前位置p不匹配,则下一次比较时模式串可以直接跳到模式串p位置的跳转位pn处, //让跳转位再跟主串当前位置继续比较。 //移动的结果是此时模式串原不匹配位置p变成跳转位pn,(此时模式串的跳转位前方跟主串对应位置是相同的)。 //如果移动后主串与模式串依然不匹配,则模式串继续移动到跳转位。其含义是——若当前prefix这个子串本身依然含有重复的前缀prefix2, //则模式串指针移动到prefix2之后的一个位置,再次比较。以此第推,直到主串结束或者模式串移动到头部(然后继续比较直到主串结束) //注意:上面提到的前缀prefix或长或短,因为比较此位置前可能此位置的值已经重复了多次 if(pre == 0 || P[lastChar] == P[frontChar]){ ++tail; //相同时前缀指针指向当前所匹配(重复)前缀的后一个位置(这个位置叫跳转位),意味着跳转位前方是某前缀的重复值, //其意义是使主串不再比较模式串中重复的前缀(而直接对齐),这也是跳转表的作用。 ++pre; next[tail] = pre;//为每一位设置跳转位 }else{ pre = next[pre];//模式串中公共前缀内部可能自身依然含有公共前缀 } lastChar = tail-1;//需要比较查重指针与前缀指针前一位置是否相同【跳转位是其前方所重复前缀的后一位置,如上图所示,当前位不匹配时跳转到其前方所重复前缀的后一位置】 frontChar = pre-1; } } int match(char S[], int sLen, char P[], int pLen, int next[]) { int i = 0, j=0; while(i < sLen && j < pLen){ if(j == 0 || S[i] == P[j]){ i++;j++; }else{ j = next[j]; } } if(j==pLen){ return i-pLen; } return 0; } //简单匹配——可用来对KMP结果验证 int match0(char S[], int sLen, char P[], int pLen) { int i = 0, j = 0; while ( i < sLen && j < pLen){ if(S[i] == P[j]){ i++;j++; }else{ i = i-j+1; j=0; } } int pos = -1;//负数表示不匹配 if (j == pLen){ pos = i - pLen; } return pos; } int main(int argc, char const *argv[]) { #define S_LENGTH 22 #define P_LENGTH 4 char P[S_LENGTH] = "abbc";//尾部 // char P[S_LENGTH] = "aaac";//中间 // char P[P_LENGTH] = "abca";//头部 char S[S_LENGTH] = "abcabaaacbcacbabaaabbc"; int next[P_LENGTH] = {0}; getNext(P, P_LENGTH, next); int pos = match(S, S_LENGTH, P, P_LENGTH, next); printf("KMP:the first matched position is %d\n", pos); int pos0 = match0(S, S_LENGTH, P, P_LENGTH); printf("SIMPLE:the first matched position is %d\n", pos0); return 0; }