KMP算法 詳解+模板


本文大部分摘自szy學長的ppt《string》中的KMP部分。

%%%膜拜szy大神orz

1.概述

KMP 算法是用來解決單模匹配問題的一種算法。

如果暴力的進行單模匹配,那么時間復雜度為O(nm)。

KMP 算法通過對模式串的預處理優化了復雜度。

2.求next數組

為了敘述方便,設模式串長度為n,主串長度為m。

將模式串稱為s1,主串稱為s2,下標從1 開始。

我們首先對模式串預處理出一個next 數組。

next[i] 表示最大的x,滿足s1[1 : x - 1] 是s1[1 : i - 1] 的后綴。

這個數組記錄了失配時,模式串指針移動的目標位置。

求next[i] 時,考慮維護一個位置j,初始時為next[i - 1]。

如果s1[j] = s1[i -1],那么next[i] 顯然等於j + 1。

如果s1[j] != s1[i - 1],那么此時需要將j 向前移動到next[j] 的位置。

一直將j 移動到next[j] 的位置,直到j = 0 或s1[j] = s1[i - 1]。

此時next[i] 等於j + 1。

由於next 是最長公共前后綴,因此在j 的移動過程中一定會經過next[i] - 1 的位置。

 1 void getnx()
 2 {
 3     nx[1]=0;
 4     for(int i=2,j=1;i<=n;)
 5     {
 6         nx[i]=j;
 7         while(j&&s1[j]!=s1[i])j=nx[j];
 8         j++,i++;
 9     }
10 }

3.匹配

在匹配過程中,設在主串中匹配到位置i,模式串中匹配到位置j。

首先如果s2[i] = s1[j],當前位置匹配成功,此時可以把i 和j 同時移動到下一個位置。

否則發生失配,需要進行調整,我們將j 置為next[j],然后繼續匹配。

同樣由於next 是最長公共前后綴,因此在j 的移動過程中不會跳過可能匹配的位置。

並且模式串中j 之前的部分一定可以匹配。

void kmp()
{
    for(int i=1,j=1;i<=m;)
    {
        while(j&&s1[j]!=s2[i])j=nx[j];
        if(j==n)
        {
            // 此時找到了一個能夠匹配的位置 
            j=nx[j];
        }
        else j++,i++;
    }
}

可以發現兩部分代碼有很大相似之處。

其實可以把求next 數組過程看做用模式串與自身匹配的過程。

4.時間復雜度

在求next 的過程中,j 指針每向后移動一步,i 指針就會向后移動一步。

而j 指針每延next 移動一次,就會向前移動大於等於一步。

由於i 指針會向后移動O(n) 次,因此j 指針也只會向后移動O(n) 次,因此向前同樣最多移動O(n) 次。

因此求next 數組部分復雜度為O(n)。

與之類似,可以得出匹配過程的復雜度為O(m)。

因此KMP 算法的總復雜度為O(n + m)。

尾聲:

總之,KMP算法是處理字符串匹配問題的一大利器。

搭配字符串上的DP可以說是......咳咳......很有趣......

(下篇高能預告)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM