1. 程式人生 > >串-模式匹配-MP演算法

串-模式匹配-MP演算法

之前學習了KMP演算法,現在學習一下它的弱化版:MP演算法。

為啥還要學習它呢?因為它是接下來要學習的AC-自動機的基礎。

輸入:主串S,子串T

輸出:主串中子串第一次出現的位置(0-length(S-1))。匹配不到不輸出.

樣例:

S:ababcabcacbab

T:abcac

執行結果:

演算法思想:

假定在匹配的過程中正在比較文字串*位置的字元和模板串abbaaba的最後一個字元。發現二者不同(稱為失配),這時,樸素演算法會把模板串後移一位,重新比較abbaaba的第一個字元和文字串!!位置的字元。

MP演算法認為,既然!!位置已經比較過一次,就不應該再比一次了,事實上,我們已經知道灰色部分就是abbaab,應該可以直接利用模板串本身的特性判斷出右移一位一定不是匹配。同理,右移兩位或者三位也不行,但是右移四位是有可能的。這個時候,需要比較*處的字元剛和abbaaba的第三個字元。

下面其中編號為i的結點表示已經匹配了i個字元,匹配開始時當前狀態是0,成功匹配時狀態加1(表示多匹配一個字元)

而失配時沿著失配邊走。用失配函式f[i]表示狀態i失配時應轉移到新狀態,要特別注意的是f[0]=0.

void find(char *T, char *P, int *f)
{
    int n, m, i, j;

    n = strlen(T);
    m = strlen(P);
    getFail(P, f);
    j = 0;                             //當前結點編號,初始為0號結點
    for(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);     //找到了
    }
}

演算法分析:

每次j++的時候會伴隨一次i++,而每次j=f[j]的時候j至少會減1.最壞情況下j增加了n次,因此j=f[j]的次數不會超過n。

因此總時間複雜度O(n)。

狀態轉移圖的構造是MP演算法的關鍵。也是它最巧妙的地方。

演算法的思想是:用自己匹配自己。根據f[0],f[1],f[i-1]遞推f[i]。

//狀態轉移圖,用自己匹配自己,用f[0],f[1]..f[i-1] 遞推f[i].
void getFail(char *P, int *f)
{
    int m, i, j;

    f[0] = 0;
    f[1] = 0;                               //遞推邊界初值
    m = strlen(P);
    for(i = 1; i < m; i++)
    {
        j = f[i];
        while(j && P[i] != P[j])
            j = f[j];
        f[i+1] = (P[i] == P[j]) ? (j+1):0;
    }
}