1. 程式人生 > >數據結構(三)串---KMP模式匹配算法之獲取next數組

數據結構(三)串---KMP模式匹配算法之獲取next數組

要求 求值 直接 都是 malloc image turn src 計算

(一)獲取模式串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=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=6時的next值,此時子串為‘ababa‘,子串中前綴是p1..pm,後綴是pm+1..pj-1,因為前面匹配成功,所有m++,m=3所以前綴為p1p2p3,後綴為p3p4p5

技術分享圖片

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

技術分享圖片

步驟六:獲取j=7時的next值,此時子串為‘ababaa‘,子串中前綴是p1..pm,後綴是pm+1..pj-1,因為前面匹配成功,所有m++,m=4所以前綴為p1p2p3p4,後綴為p3p4p5p6

技術分享圖片

技術分享圖片

步驟七:獲取j=8時的next值,此時子串為‘ababaaa‘,由於上面失配了,所以m=next[7]=2,所以我們前綴為p1p2,後綴為p6p7

技術分享圖片

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

技術分享圖片

步驟七:獲取j=9時的next值,此時子串為‘ababaaab‘,由於上面失配了,所以m=next[8]=2,所以我們前綴為p1p2,後綴為p7p8

技術分享圖片

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

技術分享圖片

註意:有可能模式串只有一個字符進行匹配,那麽我們之前說的next[2]=1也需要我們去匹配一遍,而不是直接獲取結果

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]下標 } 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;
}

技術分享圖片

數據結構(三)串---KMP模式匹配算法之獲取next數組