由HDU3689,得KMP演算法與乙個經典概率問題

2021-07-16 03:30:25 字數 3412 閱讀 3582

考慮乙個事件,它有兩種概率均等的結果。比如擲硬幣,出現正面和反面的機會是相等的。現在我們希望知道,如果我不斷拋擲硬幣,需要多長時間才能得到乙個特定的序列。

序列一:反面、正面、反面

序列二:反面、正面、正面

首先,我反覆拋擲硬幣,直到最近的三次拋擲結果形成序列一,然後我記下這次我拋擲了多少次才得到了我要的序列。重複執行這個過程,我可以算出得到序列一平均需要的拋擲次數。同樣地,反覆拋擲硬幣直到序列二產生,它所需要的次數也有乙個平均值。你認為這兩個平均值哪乙個大哪乙個小?換句話說,出現序列一平均所需的拋擲次數少還是出現序列二平均需要的次數少?

大多數人會認為,兩個序列會以同樣快的速度出現,因為在所有「正」和「反」的8種三元組合裡,「反正反」和「反正正」各佔1/8,其概率是均等的。而事實上,我們將會看到擲出序列二所需的次數更少一些。不妨考慮這樣乙個問題:在由「正」和「反」構成的n位01序列中,有多少個序列以序列一結尾但之前不曾出現過序列一?有多少個序列以序列二結尾但之前不曾出現過序列二?當n比較小時,兩者答案是一樣的(例如n=3時符合要求的情況都是唯一的),但到後來n越大時,兩者的差距越明顯:後者的個數總比前者的個數要多一些。不妨看一看n=6的情況。對於序列一,只有以下5個序列是符合要求的:

但對於序列二來說,符合條件的序列就有7個:

你可以通過計算機程式設計列舉,計算一下n為其它值的情況。計算結果和剛才也一樣:在n位01序列中,以序列二結尾但之前不含序列二的情況不會少於以序列一結尾但之前不含序列一的情況。這說明,拋擲第n次硬幣後恰好出現了序列二,其概率不會小於恰好出現序列一的概率。顯然,當n漸漸增大時,這個概率應該呈下降趨勢;同時,隨著n的增長,兩個序列各自出現的概率由相等開始慢慢拉開差距,第n次拋擲產生序列二的概率下降得要緩慢一些,或者說更多的情況集中發生在n更小的時候。因此總的來說,出現序列二所需要的拋擲硬幣次數的期望值更小。

雖然我們通過一系列的觀察驗證了這個結論,並且我們也相信這個結論是正確的(雖然沒有嚴格的證明),但我們仍然不是很接受這個結論。這種情況是有悖於我們的直覺的,它與我們的生活經驗不相符合。此刻,我們迫切需要乙個解釋,來說明這種出人意料的反常現象產生的原因。

如果不親自做幾次試驗的話,你很難體會到這種微妙的差距。考慮整個遊戲的實際過程,「反正正」序列顯然會出現得更早一些。假如某一次我們得到了序列「反正」。如果我們需要的是「反正反」序列,那麼下一次拋擲結果為反面將結束本輪的拋擲,而下一次是正面則前功盡棄,你必須再次從零開始。如果我們需要的是「反正正」序列,那麼下一次拋擲結果為正面將結束本輪的拋擲,而下一次是反面的話我至少不會慘到一切歸零,這相當於我已經有了乙個反面作為新的開頭,只需再來兩個正面即可。這樣看的話,提前擲出「反正正」的可能性更大一些。

反覆體會上面的想法,了解kmp演算法的網友會恍然大悟:這就是kmp演算法的基本思路!考慮這樣乙個問題:我們在當前字串中尋找子串「反正正」第一次出現的位置。假如當前已經能匹配模式串的前兩個字「反正」,主串中的下乙個字是「正」則匹配成功,主串的下乙個字是「反」則將使模式串的當前匹配位置退到第乙個字。考慮乙個更複雜的例子:我們希望在主串中尋找子串abbaba,現在已經在主串中找到了abbab。如果主串下乙個字元是a,則成功匹配;如果主串下乙個字元是b,則模式串最多能匹配到的位置退到了第三個字元,我只需要從abb開始繼續匹配,而不必一切從頭再來。

我們可以用kmp演算法完美地解決上面的問題。首先預處理出乙個陣列c,c[i,0]表示模式串匹配到了第i個字元,主串下乙個字元為0(反)時,模式串的匹配位置將退到**;同樣地,c[i,1]表示模式串匹配到了第i個字元,主串下乙個字元為1(正)時,新的模式串匹配位置在什麼地方。設f[i,j]表示第i次拋擲硬幣後恰好匹配到模式串第j位有多少種情況,則f[i,j]=σf(i-1,k) + σf(i-1,l),其中k滿足c[k,0]=j,l滿足c[l,1]=j。將f[i,j]除以2的i次方,我們就得到了相應的概率值。或者更直接地,設p[i,j]表示第i次拋擲硬幣後,最遠能匹配到的模式串位置是第j位的概率,則p[i,j]=σ( p(i-1,k)/2 ) + σ( p(i-1,l)/2 )。注意,我們還應該新增一種特殊的概率值p[i,*],它表示在主串第i個字元以前已經成功匹配過的概率,這樣的話下表中每一列的和才能為1。

來看一看程式的輸出結果:

pattern 1: s="aba"

主串位置       1        2       3       4       5       6       7       8       9      10

匹配到s[0]  0.5000  0.2500  0.2500  0.2500  0.2188  0.1875  0.1641  0.1445  0.1270  0.1113

匹配到s[1]  0.5000  0.5000  0.3750  0.3125  0.2813  0.2500  0.2188  0.1914  0.1680  0.1475

匹配到s[2]  0.0000  0.2500  0.2500  0.1875  0.1563  0.1406  0.1250  0.1094  0.0957  0.0840

匹配到s[3]  0.0000  0.0000  0.1250  0.1250  0.0938  0.0781  0.0703  0.0625  0.0547  0.0479

已找到匹配  0.0000  0.0000  0.0000  0.1250  0.2500  0.3438  0.4219  0.4922  0.5547  0.6094

pattern 2: s="abb"

主串位置       1        2       3       4       5       6       7       8       9      10

匹配到s[0]  0.5000  0.2500  0.1250  0.0625  0.0313  0.0156  0.0078  0.

0039  0.0020  0.0010

匹配到s[1]  0.5000  0.5000  0.5000  0.4375  0.3750  0.3125  0.2578  0.2109  0.1719  0.1396

匹配到s[2]  0.0000  0.2500  0.2500  0.2500  0.2188  0.1875  0.1563  0.1289  0.1055  0.0859

匹配到s[3]  0.0000  0.0000  0.1250  0.1250  0.1250  0.1094  0.0938  0.0781  0.0645  0.0527

已找到匹配  0.0000  0.0000  0.0000  0.1250  0.2500  0.3750  0.4844  0.5781  0.6563  0.7207

這下我們可以清楚地看到,序列二提前出現的概率要大得多。注意到,根據我們的概率定義,**中每一列的數字之和都是1。同時,倒數第二行的數字之和(有無窮多項)也應該為1,因為最後一行的概率就是倒數第二行的概率值累加的結果,而根據最後一行概率的定義,主串無窮長時已找到匹配的概率應該為1。因此,我們也可以把倒數第二行看作是模式串在主串第i個位置首次匹配成功的概率。我們可以根據這一結果近似地計算出拋擲次數的期望值。

matrix67原創

轉貼請註明出處

hdu4333 擴充套件kmp演算法

原題鏈結 思路比較簡單。把原串擴大一倍,然後以原串為模板串,用擴充套件kmp演算法求出每個位置的最大字首即可。wa了一天了,也不知道改了哪個地方就對了 include include define maxl1000100 using namespace std char s1 maxl 2 s2 m...

由字串匹配到KMP演算法

字串匹配問題就是在指對於兩段字串,待匹配串和模式串,尋找待匹配串中模式串是否出現,及出現的位置。例如在文字編輯中,我們經常要在一段文字中某個特定的位置找出某個特定的字元或模式。1 簡單字串匹配 bf brute force演算法 這個演算法簡單粗暴,就是從左邊對齊乙個個字元對比,相同就對比下一位,不...

Hdu 2087 kmp演算法的運用

description 一塊花布條,裡面有些圖案,另有一塊直接可用的小飾條,裡面也有一些圖案。對於給定的花布條和小飾條,計算一下能從花布條中盡可能剪出幾塊小飾條來呢?input 輸入中含有一些資料,分別是成對出現的花布條和小飾條,其布條都是用可見ascii字元表示的,可見的ascii字元有多少個,布...