關於KMP演算法當中的next函式
首先先貼出KMP演算法的框架程式碼,這段程式碼使用C語言當中的字串資料結構,因此字串當中第一個字元的下標為零。
int Index(constchar* str1,constchar* str2,int pos){
int* nextFunc = get_next(str2);
int strLen = strlen(str1);
int subLen = strlen(str2);
int i = pos,j =0;
while (i < strLen && j < subLen)
{
if (j ==-1
{
i++;
j++;
}
else
j = nextFunc[j];
}
if (j == subLen)
return i - subLen;
return-1;
}
相比較那種最簡單的演算法而言這裡的神奇之處在於一個next函式,由於這個next函式的存在導致我們在模式匹配過程當中某個字元出現失配的情況時不再需要回溯主串當中的指標i到開始匹配時的位置。所有的資料結構或者演算法的書都告訴我們說,之所以不需要回溯這個i指標是因為在匹配過程當中產生了一些附加的資訊,利用這些附加資訊就可以得到這樣的效能改進。
首先我們必須搞清楚這個神奇的next函式的含義。next[j]=k 這樣一個式子表示的含義是,當主串當中第i個元素與模式串當中第j個元素不匹配時我們應該保持i指標不動而將模式串當中的j指標移動到k這個位置,然後再比較主串的第i個元素與模式串的第k個元素是否匹配,匹配當然沒話說照最傳統的演算法移動兩個指標比較下一個元素或者得到完全匹配的結果,不匹配那麼再做那個動作,也就是求next[k]=?,然後再比較。
之所以存在這麼一個next函式是因為,如果說主串與模式串在匹配過程當中主串的第i個元素與模式串的第j個元素不同,那麼隱含的意義是主串的從第i-j+1個元素到第i-1個元素與模式串的第1個元素到第j-1個元素是相同的。那麼如果說這樣不能達到最後全部匹配的結果也就是上面講的主串[i] != 模式串[j],那麼我們應該從主串的i-j+1到i-1這個字串當中從後到前尋找一個最大子串與模式串的第1到j-1這個字串的從第一個到某個元素的最大子串完全匹配。而我們又知道主串中第i-j+1個元素到第i-1個元素的子串事實上就是模式串當中第1個元素到第j-1個元素所形成的子串。next函式所完成的工作就是這個尋找匹配的工作,他的返回值就是這個子串的最後一個元素的下一個位置。為什麼是這個位置,前面講的很清楚,就是說既然前面那一串匹配,那麼接下來要比較的就是這個位置的元素。下面開始描述next函式的求法。
從上面的描述我們可以知道next函式的值完全只與模式串相關而與主串是什麼樣子的沒有任何關係,因此來說對於每個模式串都有一個唯一的next串值。求法是這樣,如果說自變數為0也就是說第一個元素的next值固定為-1(-1的意思是說,主串的當前位置元素與模式串第一個元素的前一個元素匹配,隱含意義是講主串指標必須往後移一位再與模式串的第一個元素比較);如果next[j] = k也就是說模式串的第1個元素到第k個元素與第j-k+1個元素到第j-1個元素相等相等(可以按照上面的方法推出到主串上哪幾個元素),而且有模式串[j] == 模式串[k]那麼可以得到next[j+1] = k+1(這裡的原理顯而易見);如果不等那麼就求另外一個最大子串,方法就是j = next[k],然後再回到上面的比較。而其他的情況就視作next值為0(事實上只有求j=1時的next值才會出現,所以next值的前兩個元素固定是0和1)。具體演算法如下:
int* get_next(constchar* str){
int strLen = strlen(str);
int* nextFunc =newint[strLen];
if (!nextFunc)
return0;
nextFunc[0] =-1;
int i =0,j = nextFunc[i];
while (i < strLen)
{
if (j ==-1|| str[i] == str[j])
{
i++;
j++;
nextFunc[i] = next[i-1] + 1;
}
else
j = nextFunc[j]; }
return nextFunc;
}
注:本文所有的串表示方法都是C語言的預設表示方法,也就是第一個元素的下標為0