數據結構之KMP

KMP主要用於字符串匹配,比如給一個長度爲n的字符串s,然後再給一個長度爲m的字符串t,問s中有沒有連續子串和t一樣(n>=m)

顯然,直接暴力求解,容易得到最壞O(m*(n-m))複雜度的算法。當這個樸素算法的複雜度不滿足要求的時候,就需要用到我們經典的字符串匹配算法——KMP,複雜度爲O(m+n)

如下圖所示,i指針指向字符串s,j指針指向t,當i從第一個字符a開始與t匹配,當i指到紅框裏面的那個c,j指向d,發現c和d不匹配(稱爲失配)。按照暴力的方法,此次失配後,i將回到s的第二個字符b,而j回到t的起點,依次匹配,但實際上,因爲我們已經知道了i前面是abccab,也就是說i往前移只會重複匹配,很多情況下並不會匹配成功——由圖中可知,i最多要向前移動兩位,纔會使得s[i]==t[0],但這時j要指向t的開始——實際上i向前移動兩位也是一種重複匹配,還是因爲我們已經知道i前面的字符是abccab——顯然,i和j之前都是完全匹配的,爲了不重複匹配,i是不能前移的,那麼只能讓j前移了,假設j前移到了k位,正好和i匹配了,也就是說abccab的前k-1位和後k-1位完全吻合,並且abccab的前k位即t[k]要等於s[i],所以j就移到了那個c處

可以說理解了上面那個圖,就理解了KMP

額,要是不能理解,我也扯不下去了(我的博客大概是全網說KMP的說的最短的了吧~)這玩意兒好難講啊,還是直接上代碼吧~~~

// 在字符串T裏找P,輸出所有匹配點
#include <iostream>
#include <cstring>

using namespace std;
const int sz = 1010;
int f[sz];
void getFail(char* p, int* f) // 由p預處理處f[],f[x]表示上文中的j指針跑到x時發現不匹配,應該跳到f[x]處繼續匹配
{
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++ i)
    {
        int j = f[i];
        while(j && p[i] != p[j])    j = f[j];
        f[i+1] = p[i] == p[j] ? j+1 : 0;
    }
}
void find(char* T, char* P, int* f)//KMP匹配過程
{
    int n = strlen(T), m = strlen(P);
    getFail(P, f);
    int j = 0;
    for(int i = 0; i < n; ++ i)
    {
        while(j && P[j] != T[i])    j = f[j];
        if(P[j] == T[i])    ++ j;
        if(j == m)
            printf("%d\n", i-m+1);
    }
}
int main()
{
    char P[sz] = "", T[sz] = "";
    scanf("%s%s", T, P);
    find(T, P, f);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章