(一)獲取模式串T的next數組值
1.回顧
我們所知道的KMP算法next數組的作用
next[j]表示當前模式串T的j下標對目標串S的i值失配時,我們應該使用模式串的下標為next[j]接着去和目標串失配的i值進行匹配
而KMP算法的next求值函數

我們可以知道next除了j=1時,next[1]為0,其他情況都是比較前綴和后綴串的相似度(第三種情況是當相似度為0時,next值為0+1=1)
next數組,是用來評判前后綴的相識度,而next值,則是等於相似度加一
2.思考
雖然我們知道是比較前后綴的相似度,但是我們如何確定前后綴位置來獲取next值。---->pj的next值取決於 前綴p1p2....pk-1 后綴pj-k+1.....pj-1 的相似度,next值是相似度加一
pj的next值取決於 前綴p1p2....pk-1 后綴pj-k+1.....pj-1 的相似度,是相似度加一。 我們將k-1=m,其中m就是相似度,k就是next數組值-->Max{K} pj的next值取決於 前綴p1p2....pm 后綴pj-m.....pj-1 的相似度,是相似度加一。
那么我們現在的任務,就由找k-1變為找m,找相似度
例如:
雖然我們可以直接看出abab的相似度是2,
也可以編寫函數獲取到其相似度,
而且當我們求下一個next值時,串變為ababa,這時我們也可以看出相似度為3,使用同一個函數可以實現獲取到相似度。
但是我們這個函數大概就是從頭或尾開始索引,進行判斷。
每次我們獲取到了子串都要交給這個函數從頭到尾去索引獲取相似度,似乎不划算,我們是不是應該有更好的方法增加程序的性能?
3.下面我們嘗試獲取下面的T串的所有next值,從中找到關聯

步驟一:由上一篇博文可以知道前j1,j2前兩個的next是固定值為0,1

步驟二:獲取j=3時的next,此時子串只有'ab',所以子串的前綴只能選擇'a',后綴只能選擇'b';下面我們對前后綴進行匹配
next數組,是用來評判前后綴的相識度,而next值,則是等於相似度加一
next[j]表示當前模式串T的j下標對目標串S的i值失配時,我們應該使用模式串的下標為next[j]接着去和目標串失配的i值進行匹配

注意:匹配完畢后后綴會向下加一
步驟三:獲取j=4時的next值,此時子串為'aba',子串中前綴是p1..pm,后綴是pm+1..pj-1,若是m取一,此時子串的前綴可以選擇p1,后綴選擇p2;若是m=2前綴選擇p1p2后綴選擇p2p3;那么具體如何選擇這個m值呢?

重點:這個m值取決於上次失配時的next[]值,即上次j=3是失配了,所有m=next[3]=1,所以我們選取的前綴為p1='a',后綴為pj-1是'a'

根據匹配處的相似度或者下標J=1都可以得出next[4]=2
步驟四:獲取j=5時的next值,此時子串為'abab',子串中前綴是p1..pm,后綴是pm+1..pj-1,若是m取一,此時子串的前綴可以選擇p1,后綴選擇p2;若是m=2前綴選擇p1p2后綴選擇p2p3,若m取3,前綴為p1p2p3后綴為p2p3p4;那么具體如何選擇這個m值呢?

重點:若是上次匹配成功。並未失配,那么我們的m值在上一次的基礎上加1。所以這次m=2,我們選取前綴p1p2和后綴p3p4

根據匹配處的相似度或者下標J=2都可以得出next[5]=3
步驟五:獲取j=6時的next值,此時子串為'ababa',子串中前綴是p1..pm,后綴是pm+1..pj-1,因為前面匹配成功,所有m++,m=3所以前綴為p1p2p3,后綴為p3p4p5

因為前面匹配成功,所有m++,m=3所以前綴為p1p2p3,后綴為p3p4p5

根據匹配處的相似度或者下標J=3都可以得出next[6]=4
步驟六:獲取j=7時的next值,此時子串為'ababaa',子串中前綴是p1..pm,后綴是pm+1..pj-1,因為前面匹配成功,所有m++,m=4所以前綴為p1p2p3p4,后綴為p3p4p5p6


根據匹配處的相似度或者下標J=1都可以得出next[7]=2
步驟七:獲取j=8時的next值,此時子串為'ababaaa',由於上面失配了,所以m=next[7]=2,所以我們前綴為p1p2,后綴為p6p7

由於上面失配了,所以m=next[7]=2,匹配前綴p1p2,和后綴p6p7

根據匹配處的相似度或者下標J=1都可以得出next[8]=2
步驟七:獲取j=9時的next值,此時子串為'ababaaab',由於上面失配了,所以m=next[8]=2,所以我們前綴為p1p2,后綴為p7p8

由於上面失配了,所以m=next[8]=2,所以我們前綴為p1p2,后綴為p7p8

根據匹配處的相似度或者下標J=2都可以得出next[9]=3
另一種方案:是直接看匹配位置的j值即可,將j值加一即可,這個是實現程序的時候的使用思路
注意:有可能模式串只有一個字符進行匹配,那么我們之前說的next[2]=1也需要我們去匹配一遍,而不是直接獲取結果
重點補充:為什么我們可以使用下標值j來表示相似度?

我們以上圖為例:
相似度是指前綴串和后綴串之間的相似程度,通過看圖,我們不難發現相似度和最后匹配的下標使一樣的。
這是因為前綴始終是以下標為一的字符開始匹配,所以匹配到下標為多少,那他的相似度就是多少
4.代碼實現
//通過計算返回子串T的next數組 void get_next(String T, int* next) { int m, j; j = 1; //j是后綴的末尾下標 pj-m...pj-1 其實j-1就是后綴的下標,而j就是我們要求的next數組下標 m = 0; //m代表的是前綴結束時的下標,也就是相似度,是等價的 p1p2...pm next[1] = 0; while (j < T[0]) //T[0]是表示串T的長度 {
//這個if,我們只需要考慮,如果我<后綴最后下標>前面匹配成功,現在我T[j]==T[m]也匹配成功,那么我對應的next<++j>數組值是多少? if (m == 0 || T[m] == T[j]) //T[m]表示前綴的最末尾字符,T[j]是后綴的最末尾字符 { ++m; ++j; next[j] = m; //++j后獲取的才是我們要的next[j]下標,我們要獲取next[j]處的值,就是獲取他前一個匹配時的相似度,也就是前一個匹配的下標+1 } else //else是匹配失敗的情況,就要進行回溯 m = next[m]; //若是字符不相同,則m回溯 } }
5.測試結果
int main() { int i; String s1; int next[MAXSIZE] = { 0 }; char *str = (char*)malloc(sizeof(char) * 40); memset(str, 0, 40); printf("enter s1:"); scanf("%s", str); if (!StrAssign(s1, str)) printf("1.string length is gt %d\n", MAXSIZE); else printf("1.string StrAssign success\n"); get_next(s1, next); for (i = 1; i <= StringLength(s1); i++) printf("%d ", next[i]); system("pause"); return 0; }

