關於KMP(Knuth-Morris-Pratt)算法出現的來龍去脈,其能夠解決的問題以及帶來的潛在效率提高,在書本中和網絡上能夠找到的資源實在是太多了,本文再也不贅述。html
這篇文章的主要目的,是想結合本身實際總結和心得,從另一個角度,進一步解釋KMP算法匹配中主串的指針不須要回退的緣由。算法
在以前學習KMP算法的過程當中,常常看到會以一些例子演示進行說明,好比下例,當前匹配進行到字符'e'處,失敗了。網絡
a b a d e
f g h i函數
a b a d x
a學習
接下來,通常解釋都會繼續使用此類例子來講明,匹配能夠直接從當前主串'e'處開始,即主串的指針不須要回退,只須要模式串將指針進行移位到某其位置來繼續匹配就能夠了。.net
這種演示的說明方式確實易懂,可操做性強。可是對我我的來講,很難讓我徹底拋開例子,從理論/通用性的角度上去理解爲何看起來主串的指針不須要回溯。指針
好比如今有一個主字符串T[t1, t2, t3,..., tn],模式串P[p1, p2, p3,...pm],主串和模式串的指針在第一次匹配時,都匹配到了k+1的位置,即T和P的前k個字符都是1:1匹配的,code
t1 t2 t3 ... tktk+1 ... tnhtm
p1 p2 p3 ... pkpk+1 ... pmblog
在沒有具體字符出現的狀況下,要怎麼樣解釋T在k+1處的字符串指針不須要回退到第2個字符處?
這個疑問縈繞在我腦子中好久,我也試圖經過查找大量的資料,包括《算法導論》,以及KMP三位做者共同發表的論文《Fast pattern matching in strings》,惋惜要麼基本是一筆帶過,要麼就是限於本人資質,沒有能明白其中的說明。
直到我在Quora上看到了一篇文章關於KMP解釋的文章,雖然其中主要是圍繞如何查找next(j)來進行的說明,可是給我提供了另一種思路去理解和解決個人疑問。
若是也有其餘的同窗也有與我同樣的疑惑,但願個人這篇文章可以幫助你。
簡單說一下本文涉及到的一些術語定義:
模式串P。即一般在字符串搜索中提到的子串。長度爲m,指針指示當前字符的位置定義爲j。
主串T。即被匹配字符串,在該字符串中查找可以徹底匹配上模式串的起始位置。長度爲n (n > m),指針指示當前字符的位置定義爲i。
i和j第一次匹配都從1開始進行計算。即t1表明T中的第一個字符,p1表明P中的第一個字符。
假定以前已經進行了數次匹配並失敗,當前一次的匹配是從主串的任意位置k+1處開始的,而且主串和模式串均成功匹配連續L個字符。到主串的位置(k+L+1)處匹配失敗。
那麼有
k + L + 1 = i(其中i < n); L + 1 = j (其中j < n)
t1 t2 t3 ... tk | tk+1 tk+2 tk+3 ... tk+L | tk+L+1 | ... ... ... tn |
---|---|---|---|
p1 p2 p3 ... pL | pL+1 | ... pm |
我以前的疑惑是,若是主串指針不從(k+L+1)回溯到(k+2)的位置,萬一k+2處開始模式串和主串可以匹配上,那這種成功匹配的場景,不就可能被遺漏了麼。
如今咱們來看看,假定在(k+2)處模式串和主串真的有成功匹配的可能,那麼基於咱們已知的信息,必然須要以下內容成立:
[p1, p2, p3, ..., pL-1 ] == [tk+2, tk+3, tk+4, ..., tk+L]
繼續分析,
Alt1: 假如咱們的主串和模式串確實可以知足上述條件,那麼接下來咱們須要進行匹配的,是pL 與tk+L+1,看到了麼,此時主串的指針到了回溯前的位置;
Alt2:假如咱們的主串和模式不可以知足上述條件,那麼此時主串指針回溯到(k+2)的位置匹配過程能夠結束了,位置指針能夠從新指到(k+3)的位置,而且模式串從新回溯到1的位置,繼續看是否有主串和模式串成功匹配的可能性。
咱們再來看看,假定在(k+3)處模式串和主串真的有成功匹配的可能,那麼基於咱們已知的信息,必然須要以下內容成立:
[p1, p2, p3, ..., pL-2 ] == [tk+3, tk+4, tk+5, ..., tk+L]
怎麼樣,是否是以爲很熟悉,這是一個重複上述Alt1和Alt2分析的過程,最終的結果是兩種,要麼是此時主串的指針仍是到了k+L+1的位置,要麼就是進入主串指針進入下一輪的從(k+4)的位置開始匹配的過程。
若是上述過程可以不斷的重複進行下去,始終沒有發現匹配的,直到主串指針指到(k+L),模式串指針再一次從新回溯到了1的位置,如今咱們須要判斷以下等式是否成立的時候了:
[p1] == [tk+L]
又一次,咱們的分析結果:若是成立,那麼下一個主串和模式串要匹配的字符,就在主串指針的(k+L+1)的位置,與模式串的t2進行匹配,而此時主串的位置,正是最初從k位置開始匹配時,匹配失敗的位置;
若是等式不成立,那麼主串指針天然指向(k+L+1)的位置,而模式串須要使用p1 與 tk+L+1開始進行匹配。
從上述過程的分析,咱們能夠看出,若是主串從k+1的位置開始與模式串進行匹配,成功匹配了L個連續字符後,主串的指針不須要回退,實際上是由於:
咱們假設能夠有多輪的隱含回溯匹配過程,經過利用已知的信息[p1, p2, p3, ..., pL ] == [tk+1, tk+2, tk+3, ..., tk+L],咱們能夠徹底推斷處,無論這些輪匹配中,是否至少有一輪在主串的(k+L+1)字符以前,使得全部字符與有效的模式串字符匹配成功,最終都會使得主串指針指向(k+L+1),即i這個位置。
那麼咱們要作的,就是相對來講,不須要指針i在主串中回溯,經過調整模式串的位置指針j的位置變化(next(j)函數),來達到快速匹配的效果。