淺談 KMP 算法

最近在複習數據結構,學到了 KMP 算法這一章,彷佛又迷糊了,記得第一次學習這個算法時,老師在課堂上講得唾沫橫飛,十分有激情,而咱們在下面聽得一臉懵比,啥?這是個啥算法?啥玩意?再去看看書,徹底聽不懂呀?總之,以爲十分懵比,課後去看了一些視頻和博客,才慢慢有一點理解,學習不是一蹴而就的,須要腳踏實地的努力。過了三年,從新溫習這個算法,彷佛依舊不是很明白,理解得不夠透徹,從新拾起課本和視頻,認真學習這個算法。html

1.KMP 算法簡介算法

KMP 算法是由三位老前輩(D.E.Knuth,J.H.Morris 和 V.R.Pratt )的研究結果,該算法巧妙之處在於避免重複遍歷的狀況,全稱叫作克努特-莫里斯-普拉特算法,簡稱 KMP 算法,D.E.Knuth,編寫了《計算機程序設計藝術》寫完了第四卷,這部著做被譽爲計算機領域中的「相對論」。數組

2.子串 next 數組的計算微信

 KMP 算法關鍵點是先求出 next[] 數組,這個 next 數組只與模式匹配串有關,例如以 "abababca" 這個子串計算一下它的 next 數組數據結構

下標爲 index = 0 開始 ,tcp

index = 0 ,"a" 的前綴和後綴都爲空集,value = 0;學習

index = 1,"ab" 的前綴和後綴分別爲 "a" 和 "b",不相等,value = 0;網站

index = 2, "aba" 的前綴是 "a"、 "ab",後綴是 "ba"、"a",有相同交集 "a",長度爲 1, value = 1;spa

index = 3, "abab" 的前綴是 "a"、"ab"、"aba",後綴是 "bab"、"ab"、"b",有最長相同交集 "ab", 長度爲 2,value = 2;設計

index = 4,"ababa" 的前綴是 "a"、"ab"、"aba"、"abab",後綴是 "baba"、"aba"、"ba"、"a",有最大相同交集 "aba",長度爲 3, value = 3;

index = 5,"ababab" 的前綴是 "a"、"ab"、"aba"、"abab"、"ababa",後綴是 "babab"、"abab"、"bab"、"ab"、"b",有最長相同交集 "abab",長度爲 4, value = 4;

index = 6,"abababc" 的前綴是 "a"、"ab"、"aba"、"abab"、"ababa"、"ababab",後綴是 "bababc"、"ababc"、"babc"、"abc"、"bc"、"c",沒有相同交集,value = 0;

index = 7,"abababca" 的前綴是 "a"、"ab"、"aba"、"abab"、"ababa"、"abababc",後綴是 "bababca"、"ababca"、"babca"、"abca"、"bca"、"ca"、"a",有相同交集 "a",長度爲1,value = 1;

最後結果以下:

char:   | a | b | a | b | a | b | c | a |

index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |

value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |

三、如何使用 next[] 數組

獲得子串的 next 數組之後,在目標串中匹配使用 next 數組,經過使用 next 數組避免重複的匹配已經匹配過的元素,若是找到長度爲 partial_match_length 的部分匹配,而且表 next [partial_match_length]> 1,咱們能夠提早跳過 partial_match_length - next[partial_match_length-1] 個字符

總結移動位數  = 已匹配的字符數 - 對應的部分匹配值

char:   | a | b | a | b | a | b | c | a |

index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |

value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |

以 "bacbababaabcbab" 爲例說明它的匹配過程,第一次匹配, 調到 index = 1 位置,以下

bacbababaabcbab

  |

  abababca

不難看出, 部分匹配的長度爲 partial_match_length = 1, 可是在 next [ partial_match_length - 1] = 0,也就是 next[0] = 0,這個元素,因此咱們不須要跳過任何元素,接下來 cb 和 a 都不匹配直接向右匹配,到了下一個 a 匹配的地方

bacbababaabcbab

        | | | | |

     abababca

來到這個地方,你會發現此時部分匹配的長度爲 5 , partial_match_length = 5,  next[partial_match_length - 1] = next[4],查 next 數組,next[4] = 3,這就意味着在接下來的匹配中咱們要跳過 partial_match_length - next[partial_match_length-1] ,即 5 - next[4] = 5 - 3 = 2,要跳過 2 個字符,因此接下來的匹配應該變成了以下所示:

bacbababaabcbab

        xx | | |

         abababca

xx 表示跳過了,部分匹配長度爲 3, partial_match_length = 3,next[partial_match_length - 1] = next[2] = 1,接下來匹配中要跳過 

partial_match_length - next[partial_match_length - 1], 即 3 - 1 = 2, 跳過 2 個字符後的匹配狀況以下:

bacbababaabcbab

            xx | 

             abababca

獲得部分匹配長度爲 1 , partial_match_length = 1, next[partial_match_length - 1] = 0,接下來匹配不用跳過字符,向右匹配,匹配串比剩餘的主串要長,因此沒有找到匹配的字符串。

 

 

四、KMP 算法代碼實現,使用 C 語言實現

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void get_next(char T[],int next[])//next數組
{
    int i,j;
    i=0;//
    j=1;//
    next[1]=0;
    while(j<T[0]) {
        if(i==0 || T[i]==T[j])
        {
            i++;
            j++;
            next[j]=i;
            /*if(T[i]!=T[j])
            {
                next[j]=i;
            }
            else 
            {
 
                next[j]=next[i];
            }*/
        }
        else 
        {
            i=next[i];
        }
    }
}
int Index_KMP(char S[],char T[])
{
    int next[1000];
    int i=1;
    int j=1;
    get_next(T,next);//得到next數組
    /*
    for(i=1;i<=T[0];i++)
    {
            printf("%d ",next[i]);
    }
    */
    while(i<=S[0] && j<=T[0])
    {
        if(j==0||S[i]==T[j])
        {
           i++;
           j++;
        }
        else 
        {
            j=next[j];
        }
    }
    if(j>T[0])
        return i-T[0];
    return 0;
 
}
int main (){
    char T[1000],S[1000];
    int i,k;
    while(scanf("%s %s",S,T)!=EOF)
    {
        k=strlen(T);
        for(i=strlen(T);i>0;i--)//向後移動
        {
            T[i]=T[i-1];    
        }
        T[0]=k;
        k=strlen(S);
        for(i=strlen(S);i>0;i--)//向後移動
        {
            S[i]=S[i-1];    
        }
        S[0]=k;
        printf("%d\n",Index_KMP(S,T));
    }
    return 0;
 
}

 

運行結果以下:

 

 4 爲第一個出現匹配字符串的數字下標從 1 開始

五、我的總結

 通過此次對於 KMP 算法的練習,使我從新練習了一遍,關於 KMP 中算法實現的某些步驟依舊不是很清楚,有些地方想得還不是特別明白,也許這就是差距。今天出現了一些代碼的 Bug,爲了解決 Bug 查了一些網站的資料,從新溫習了 C語言的使用,今天過得很充實。

歡迎你們關注個人微信公衆號:

參考資料:

http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

https://liam.page/2016/12/20/KMP-Algorithm/

https://blog.dotcpp.com/a/8986

相關文章
相關標籤/搜索