在樸素的模式比對演算法中,主串的pos值(i)是不斷地回溯來完成的(見字串的基本操作中的Index函數)。而電腦 的大仙們發現這種回溯其實可以是不需要的。既然i值不回溯,也就是不可以變小,那麼考慮的變化就是子串的pos值(j)了 。通過分析發現子串中如果有相等字元,j值的變化就會不相同,也就是說,這個j值的變化跟主串其實沒什麼關係,關鍵就 取決於子串的結構中是否有重複的問題。
我們把子串各個位置的j值變化定義為一個數組next,那麼next的長度就是 子串的長度(next[0]空置)。於是可以得到下面的函數定義。
範例程式碼:(改編自《大話資料結構》)
#include<iostream>using namespace std; #define MAXSIZE 30typedef char String[MAXSIZE + 1]; //以'\0'結尾/* 產生一個串*/bool StrAssign(String Dest, char *ptr){ cout << "Assign Str ..." << endl; int i; for (i = 0; ptr[i] != '\0' && i < MAXSIZE; i++) Dest[i] = ptr[i]; Dest[i] = '\0'; return true;} int StrLength(String Src){ int i = 0; while (Src[i] != '\0') i++; return i;} void StrPrint(String Src){ cout << "Print Str ..." << endl; for (int i = 0; Src[i] != '\0'; i++) cout << Src[i]; cout << endl; }/* 通過計算返回子串Sub的next數組。 */void GetNext(String Sub, int *next){ int i = 1; int j = 0; next[1] = 0; while (i < StrLength(Sub)) { if(j == 0 || Sub[i - 1] == Sub[j - 1]) { ++i; ++j; next[i] = j; } else j = next[j];/* 若字元不相同,則j值回溯 */ }} void GetNextVal(String Sub, int *nextval){ int i = 1; int j = 0; nextval[1] = 0; while (i < StrLength(Sub)) { if(j == 0 || Sub[i - 1] == Sub[j - 1]) { ++i; ++j; if (Sub[i - 1] != Sub[j - 1]) /* 若當前字元與前置詞字元不同 */ nextval[i] = j;/* 則當前的j為nextval在i位置的值 */ else nextval[i] = nextval[j];/* 如果與前置詞字元相同,則將前置詞字元的 */ /* nextval值賦值給nextval在i位置的值 */ } else j = nextval[j]; }}/* 返回子串Sub在主串Src中第pos個字元之後的位置。若不存在,則函數傳回值為0。 */int IndexKMP(String Src, String Sub, int pos){ cout << "KMP Index ..." << endl; int i = pos - 1; int j = 0; int next[10]; int len1 = StrLength(Src); int len2 = StrLength(Sub); /*GetNext(Sub, next);*/ GetNextVal(Sub, next); while (i < len1 && j < len2) { /* 兩字母相等則繼續,與樸素演算法增加了j=0判斷 */ if (j == 0 || Src[i - 1] == Sub[j - 1]) { ++i; ++j; } else { j = next[j];/* j退回合適的位置,i值不變 */ } } if (j == len2) return i - len2 + 1; else return 0;} int main(void){ String Str1, Str2, Str3, Str4; StrAssign(Str1, "defewabcabxwfew"); StrAssign(Str2, "abcabx"); StrAssign(Str3, "ababaaaba"); StrAssign(Str4, "htrhdhsgtrhababaaabafwrew"); int next[10]; //next[0]空置 GetNext(Str2, next); cout << "Get Next array ..," << endl; for (int i = 1; i < 7; i++) cout << next[i]; cout << endl; GetNext(Str3, next); cout << "Get Next array ..," << endl; for (int i = 1; i < 10; i++) cout << next[i]; cout << endl; GetNextVal(Str3, next); cout << "Get NextVal array ..," << endl; for (int i = 1; i < 10; i++) cout << next[i]; cout << endl << endl; cout << IndexKMP(Str1, Str2, 1) << endl; cout << IndexKMP(Str4, Str3, 1) << endl; return 0;}
輸出為: