16 - 12 - 20 KMP算法 模式匹配 終極奧義!

尊重產權,轉載請註明出處。

ex:找一個單詞在一篇文章中的定位 問題 :這種子串的定位操作
通常叫做 串的模式匹配。
ex 我們要從 goodgoogle 中 找 google 這個單詞。
樸素的方法是:
1、取子串的第一個字符叫做子首,主串 向後查找,
直到找到第一個與子首相等的字符叫做主首;

2、子首後移一位,主首後移一位,再次比較(重複此過程)
要是直到查找了n次(n = 子串長度),仍然是次次對應相等,
說明子串就是主串真正的子串。

3、比如:
主串:goodgoogle;
子串:google;主串一開始與子串比較,子串比較到 g-d 發現不相等了,指向主串的指針退回到第二個字符o再重複2過程,

ex-2 : (設 n 爲主串長度,m 爲子串長度):
主串:00000000000000000000000000000000000000000001 n=44
子串:0000000000000000001 m= 19;
需要比較的次數爲:(n-m)*m (比較簡單)
其實這是完全沒有必要的,因爲 當比較到 g-d 時候,
我們就能知道前面的六個字符肯定都不符合,
根本不需要退回到主串的第二個字符重新搜索

那,有人會問,幹嘛非要討論這種極端情況呢?
不,其實真的很有必要!
因爲當m 越小,出現這種低效匹配的概率越高,
而若當m 越大,所導致的後果也就越嚴重。

不過,蠻力算法也並不是一無是處,比如在實際情況如搜索引擎裏,
n是對應着需要搜索的庫,m是用戶輸入的關鍵字,
n相對m來說是遠遠大於m的,所以O(n-m+1)*m 也等於 O(n),
極端情況的概率也就變得很小了。

*************************KMP講解:****************************

比如:比較 abcdefgab 和 abcdex

仔細觀察發現:子串的首字符a 與其後的bcdex 都不相等,
那麼對於主串abcdefgab 的 與子串對應相等的前五位而言已經失去了比較的意義。主串只需要直接跳轉到f 開始比較就行。
那麼主、子串要是這樣:
S = abcabcabx
T = abcabx 觀察到子串中T1 = T4, 這種情況怎麼辦呢?
可以確定S2 S3肯定是不用再次比較了,所以T只需要與S4 S5 對齊即可。

子串如何滑動與主串是沒有關係的,因爲出現失配的前面 肯定都是匹配的,
所以指向主串的計數變量 i 不動,指向模式串(子串)的計數變量 j 會根據next 來確定滑動距離。
下面我們就以失配前匹配的那一段前綴來構建next[ ]數組以期確定下一次子串滑動的方向

那麼next 數組究竟啓什麼作用?

返回失配位之前的最長公共前後綴!


具體來講,KMP算法想要解決的主要矛盾是子串的自匹配所造成的重複冗餘。



↑ 其中一律取 0 :就是從模式串的第一個字符開始 重新比較唄,
模式串中 沒有重複的部分,那我們的KMP 也就無用武之地了。

說了這麼多,那如何構建一個next[]數組呢?

<<<<<<<<<<代碼君>>>>>>>>>>>

int main() {

    int next[10]; int pos = 0;
    char T[MAXSIZE],S[MAXSIZE];
    printf("Please input the main string and the model string \n");
    while(scanf("%s%s",S,T)) {
        Getnext(T,next);
        int result = KMP(S,T,pos,next);
        if(result)
            printf("We found it :%d \n\n",result);
        else
            printf("NO FOUND!v\n\n");
    }


    system("pause") ;
    return 0;
}

這裏寫圖片描述

/* 在主串 S 的第 pos 個位置開始查找子串 T,返回模式串在主串中的位置*/


int Getnext(char *T,int next[]) {
    int j = 0 , k = -1;int tlen = strlen(T);
    next[0] = -1;
    while( j < tlen) {
        if(k == -1 || T[j] == T[k] ) {
            j++;
            k++;
            next[j] = k;
        } else
            k = next[k];
    }
}


int KMP(char *S,char *T,int pos,int next[]) {
    int i = pos - 1;
    int j = 0;
    int slen = strlen(S);
    int tlen = strlen(T);
    while(i < slen && j < tlen) {
        if(j == -1 || S[i] == T[j]) {
            i++ ;
            j++ ;
        } else
            j = next[j];
    }
    if(j >= tlen)
        return i - tlen + 1;
    else
        return -1;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章