KMP題目分析與總結
前言
本篇文章不闡述KMP演算法流程與實現方式,網上有很多對KMP演算法進行詳細闡述的文章,至於KMP的實現方式,特別推薦劉汝佳版本的KMP演算法,簡單易懂且易於實現。
本篇文章主要針對常見的KMP問題進行剖析,提供一些思路供反思、參考、交流。
KMP能夠解決的問題
KMP可以有效解決大部分單匹配問題,KMP演算法的複雜度是O(M+N),這是十分高效的。
KMP核心問題——F陣列(NEXT陣列)
F陣列是整個KMP問題的核心(劉汝佳版本的KMP稱為F陣列,大部分KMP資料稱為NEXT陣列)。幾乎所有KMP題目都是針對F陣列來進行考察。
F陣列實質上是針對KMP演算法的狀態轉移圖進行的一個詮釋,F陣列的意義在於某一字元失配以後,跳轉到可能匹配的位置繼續進行匹配,從而節省了時間。
KMP問題分析思路一——打表找規律
幾乎所有的KMP題目都是在F陣列上做文章,實際上F陣列能做的花樣並不是很多,所以將F陣列打表,觀察樣例和表之間的聯絡,就很有可能找到規律,從而解決問題。
例題一:POJ 2752
問一個字串,字首和字尾相同的字首長度可能是多少?
例如:ababcababababcabab
字首長度為2 4 9 18時,字首和字尾長度相同
解題思路:
對於這道題,剛拿到題就開始分析,其實是比較慢的。最佳的方法是將樣例的f表打一下,看有什麼規律。
打表以後,很容易就能發現f[18]=9,f[9]=4。發現這個規律以後,這道題便迎刃而解。
為了論證打表找規律的實用性,我決定再選擇一道題,進行論證和分析。
例題二:POJ 2406
給一個字串,問這個字串是由多少個子串迴圈組成
例如:ababab 該字串就是由3個ab組成
解題思路:
依然是打表,打表完成以後就能發現”ababab”的f[6]=4。n-f[n]便是迴圈子串的長度。判斷一下n能否整除迴圈子串長度,能整出答案就為整除結果。若不能整除,則答案為1。
由上面兩道例題可知,打表在KMP題目中對於發現解決方案起著至關重要的作用。
KMP問題分析思路二——推理驗證
推理驗證並不是一個很好的發現解決方案的途徑,儘管推理論證得到的方案會很合理。在大多數情況下,打表是發現解決方案的最佳途徑,而推理論證只不過是驗證解決方案的正確性。
若想要推理驗證,那邊需要對KMP演算法的F陣列有著較為深刻的理解。
在這裡,繼續選擇上面兩道例題進行推理驗證。
例題一
這道題根據打表找出的規律進行推理分析倒也不難,F陣列的作用就是如果不匹配,則轉移到可能匹配的位置,這意味著該位置與可能匹配位置的字首相同。繼續拿樣例進行分析。f[18]=9。這就意味著18號位置的字首等於9號位置以前的字元,因為只有這樣子,才可能匹配。如果18號位置的字首與9號位置以前的字元存在不等的情況,那麼匹配轉移的時候必然不會轉移到9號位置。總之,無論怎樣論證,該規律總是正確的。
例題二
這道題的論證也很容易,迴圈子串的長度就是該迴圈子串到上一個子串的長度。因此,如果長度恰好為迴圈子串的整數倍,那麼n-f[n]恰好就是一個子串的長度。從而可證明該規律正確。
通過上述兩段論證,我們可以瞭解兩道例題通過打表所尋找到的規律的正確性。推理論證不失為驗證規律的一個好辦法。
總結
本篇文章通過兩道例題,分析了KMP題目中常用的解題思路。打表找規律+推理驗證基本上可以解決大部分與KMP有關的問題。本篇文章提到的解題思路供參考交流,如果有更好的解題思路,歡迎在評論區進行評論。若文章中有錯誤的地方,也歡迎指正。