有些演算法,適合從它產生的動機,如何設計與解決問題這樣正向地去介紹。但kmp演算法真的不適合這樣去學。最好的辦法是先搞清楚它所用的資料結構是什麼,再搞清楚怎麼用,最後為什麼的問題就會有恍然大悟的感覺。我試著從這個思路再介紹一下。大家只需要記住一點,pmt是什麼東西。然後自己臨時推這個演算法也是能推出來的,完全不需要死記硬背。kmp演算法的核心,是乙個被稱為部分匹配表(partial match table)的陣列。我覺得理解kmp的最大障礙就是很多人在看了很多關於kmp的文章之後,仍然搞不懂pmt中的值代表了什麼意思。這裡我們拋開所有的枝枝蔓蔓,先來解釋一下這個資料到底是什麼。對於字串「abababca」,它的pmt如下表所示:
就像例子中所示的,如果待匹配的模式字串有8個字元,那麼pmt就會有8個值。
我先解釋一下字串的字首和字尾。如果字串a和b,存在a=bs,其中s是任意的非空字串,那就稱b為a的字首。例如,」harry」的字首包括,我們把所有字首組成的集合,稱為字串的字首集合。同樣可以定義字尾a=sb, 其中s是任意的非空字串,那就稱b為a的字尾,例如,」potter」的字尾包括,然後把所有字尾組成的集合,稱為字串的字尾集合。要注意的是,字串本身並不是自己的字尾。
有了這個定義,就可以說明pmt中的值的意義了。pmt中的值是字串的字首集合與字尾集合的交集中最長元素的長度。例如,對於」aba」,它的字首集合為,字尾 集合為。兩個集合的交集為,那麼長度最長的元素就是字串」a」了,長 度為1,所以對於」aba」而言,它在pmt表中對應的值就是1。再比如,對於字串」ababa」,它的字首集合為,它的字尾集合為, 兩個集合的交集為,其中最長的元素為」aba」,長度為3。
好了,解釋清楚這個表是什麼之後,我們再來看如何使用這個表來加速字串的查詢,以及這樣用的道理是什麼。如圖 1.12 所示,要在主字串"ababababca"中查詢模式字串"abababca"。如果在 j 處字元不匹配,那麼由於前邊所說的模式字串 pmt 的性質,主字串中 i 指標之前的 pmt[j −1] 位就一定與模式字串的第 0 位至第 pmt[j−1] 位是相同的。這是因為主字串在 i 位失配,也就意味著主字串從 i−j 到 i 這一段是與模式字串的 0 到 j 這一段是完全相同的。而我們上面也解釋了,模式字串從 0 到 j−1 ,在這個例子中就是」ababab」,其字首集合與字尾集合的交集的最長元素為」abab」, 長度為4。所以就可以斷言,主字串中i指標之前的 4 位一定與模式字串的第0位至第 4 位是相同的,即長度為 4 的字尾與字首相同。這樣一來,我們就可以將這些字元段的比較省略掉。具體的做法是,保持i指標不動,然後將j指標指向模式字串的pmt[j −1]位即可。
簡言之,以圖中的例子來說,在 i 處失配,那麼主字串和模式字串的前邊6位就是相同的。又因為模式字串的前6位,它的前4位字首和後4位字尾是相同的,所以我們推知主字串i之前的4位和模式字串開頭的4位是相同的。就是圖中的灰色部分。那這部分就不用再比較了。
有了上面的思路,我們就可以使用pmt加速字串的查詢了。我們看到如果是在 j 位 失配,那麼影響 j 指標回溯的位置的其實是第 j −1 位的 pmt 值,所以為了程式設計的方便, 我們不直接使用pmt陣列,而是將pmt陣列向後偏移一位。我們把新得到的這個陣列稱為next陣列。下面給出根據next陣列進行字串匹配加速的字串匹配程式。其中要注意的乙個技巧是,在把pmt進行向右偏移時,第0位的值,我們將其設成了-1,這只是為了程式設計的方便,並沒有其他的意義。在本節的例子中,next陣列如下表所示。
}好了,講到這裡,其實kmp演算法的主體就已經講解完了。你會發現,其實kmp演算法的動機是很簡單的,解決的方案也很簡單。遠沒有很多教材和演算法書裡所講的那麼亂七八糟,只要搞明白了pmt的意義,其實整個演算法都迎刃而解。
現在,我們再看一下如何程式設計快速求得next陣列。其實,求next陣列的過程完全可以看成字串匹配的過程,即以模式字串為主字串,以模式字串的字首為目標字串,一旦字串匹配成功,那麼當前的next值就是匹配成功的字串的長度。
具體來說,就是從模式字串的第一位(注意,不包括第0位)開始對自身進行匹配運算。 在任一位置,能匹配的最長長度就是當前位置的next值。如下圖所示。
求next陣列值的程式如下所示:
void getnext(char * p, int * next)
else
j = next[j];
}}
通俗易懂的KMP演算法詳解
1.bf演算法 首先,簡單介紹bf 簡單的模式匹配法 假設主串為 abcbcglx 長度為m,模式串為 bcgl 長度為n。通常先從主串的下標0開始與子串的下標0開始匹配,如果匹配失敗,則從主串的第二個元素開始與子串的第乙個元素進行匹配。例如 第一次匹配 剛開始先用主串下標0 a 與子串下標0 a ...
通俗易懂的EM演算法
假設現在有兩枚硬幣1和2,隨機拋擲後正面朝上概率分別為p1,p2。為了估計這兩個概率,做實驗,每次取一枚硬幣,連擲5下,記錄下結果,如下 硬幣結果統計1 正正反正反 3正 2反 2反反正正反 2正 3反 1正反反反反 1正 4反 2正反反正正 3正 2反 1反正正反反 2正 3反 可以很容易地估計出...
RocketMQ 術語詳解(通俗易懂)
rocketmq中有很多概念,其中包括一些術語和角色。理清楚基本的概念能有效的幫助理解rocketmq的原理以及排查問題。生產者。傳送訊息的客戶端角色。傳送訊息的時候需要指定topic。消費者。消費訊息的客戶端角色。通常是後台處理非同步消費的系統。rocketmq中consumer有兩種實現 pus...