詳解KMP演算法 另一種思路

2022-05-10 02:22:19 字數 4443 閱讀 6842

這個演算法單純從**理解起來比較費勁.我覺得從思路上理解是非常簡單的.

傳統演算法的劣勢很容易察覺.那就是會有重複的匹配過程.我們假定 text為待查文字, pattern 為匹配串.

text="aaaab"

pattern="ab"

按以下傳統演算法.則直到迴圈到最後一次比較.才找到"ab".而前面很多迴圈都是做的無用功.

#include #include 

typedef

const

char* const

cstr;

int findstr(cstr src,int

from

,cstr des)

p++;

}return -1

;}

而kmp演算法是想怎麼思考的呢?

是這樣.讓pattern字串裡的每乙個字元都帶上乙個標示.來標示pattern字元之間的關係.這些字元間的關係可以省下很多重複的比較.比如說

如果pattern="abcabcabc"   text="abcabx"

模擬計算機比較過程,請把下面的字串看成可左右移動的紙帶.

黑體表示從左到右的比較,並相等.紅色代表不相等發生處,維護兩個下標.代表當前對比的字串.我就不取名字了.免得你頭暈.用彩色表示.此處為紅色.

abcabcabc   

abcabx

當計算到c與x不相等時.計算機該怎麼做才最有效率呢?? 傳統演算法會這樣,將text的對比下標指向b.

abcabcabc

abcabx

kmp會怎樣?kmp會這樣...

abcabcabc

abcabx

所以kmp詭異在哪? 它為什麼要跳過去.為什麼要從pattern[2]開始比較??而不是從頭開始比較?  其實道理非常簡單,我們先回到剛開始比較時候的情況

abcabcabc   

abcabx

再具體看看pattern

abcab

x      我將ab標藍.而且加了下劃線.為什麼?因為他們是重複的!!!並且是與開頭的串重複.!! 這種重複有什麼性質呢?我們把開頭的ab稱為第一組.後面的ab為第二組.

abcab

cabc

abcab

x如果在對比完第二組ab後,當c與x對比發現了不相同時.由於第一組ab與第二組ab是相同的, 在text[5]與pattern[5]對比不相同後,將第1組ab的位置替換第二組ab的位置.再進行下乙個字元的比較. 上面示例的和下面示例的轉換是對等的.而下面的下一步將是比較text[5]與pattern[2] 也即 也即比較c 與 c. 相等.  特別要注意的是text下標在下一輪比較中下標未動.(橙色代表要比較的字元,但還沒比較 )

abcab

cabc

abcab

x如果你看到,並理解了.其實你已經懂了kmp的核心思想了.只不過要將這個思想再具體化而已了.

首先要解決的一問題.我怎麼知道在發現c與x不等時,讓pattern哪一位再來與c比較呢?從上面的示例可以看出.是pattern[2].好.我們已經解決了乙個問題了.那我們把2存哪呢??既然它與pattern[5]相關,也即與x相關.我們建乙個與pattern一樣長度的int陣列.名叫next,指定next[5]=2.

abcab

cabc

abcab

x第二個問題隨即而來,next陣列其他的數值填什麼呢? 我們先想另外乙個問題.那就是.我們什麼時候會需要next裡的值?對了.那就是當pattern與text 在對比某值發生了不相等的時候.比如上面的c跟x.現在我們將text改一下,pattern保持不變,不然next其他值用不到,kmp演算法就結束了...:) 變成如下,

abcbdcabc   

abcabx

當對比到text[3]與 pattern[3]時,發現了不同. 這個時候怎麼移動pattern呢? 很簡單.如果你看出了規律! 請按你規律寫演算法即可.而我....太蠢沒看出規律..所以.在next[3]處.我將填入乙個-1 ,意思是, pattern回到起點開始比較.而text往後移一位.如下示..哦.為什麼要移一位??首先我請問你乙個問題.

abcbdcabc  

abcabx

那就是,隨你怎麼改text串.你能得到某個text串使前面提到的移一位會出現錯誤嗎?你會發現..永遠不會..為啥 ? 我們來看一下pattern串. 其中重複的ab.a為重複串的開頭.這個資訊看起來有點重要..的確如此.看上面text[3]與pattern[3]不等.在而pattern[3]是乙個特殊的值..它是重複串ab的第乙個值.所以如果像下面所示.有意義麼...?沒有!! 肯定不等.(而且在演算法裡會產生死迴圈...動不了了...) 好了,我們現在又有了乙個規則.重複串的開關必須為-1. 現在next值又多了兩個成員next[0]= -1, next[3] =-1; 再有乙個next[5] =2.在上文已提過.

abcbdcabc  

abcabx

現在next陣列已經填了3個數了.貌似還剩下3個沒填..怎麼辦!!  其實.很簡單.如果你覺得沒有再可以優化的地方了.你就直接呼叫傳統查詢演算法.而核心思想就是pattern 回到 0 位置.text保持不動.至於怎麼在next裡表示.那隨你.~按我們現在的邏輯最好是寫個0.代表回到pattern[0].這樣最好.但記住.還要保持text別動.不是像 next[k]=-1一樣.向後移一位.

所以我們看看next這個陣列...是個什麼東西?其實就是乙個對pattern規律的乙個表示形式而已.我上面描述的規律還只總結了兩條而已.你可以總結更多條,在演算法中加已利用.我們總結了哪兩條規則?

abeeeabfabf  我們就拿這個做例子.我不喜歡說定理...一堆j k 引數看著頭暈 ..

abeeeabfabf   開頭的重複與後面的重複找出來.將next[0]=-1.將next[5]=-1;將next[8]=-1 ,將next[7]=2 將next[10]=2;  next其他的值填為0.

則得到.

next=;

少年.你學會kmp了...你可以發揮你的才能繼續擴充套件kmp.因為0放在那有些浪費 ....

給乙個規則更多的求next的 函式 看它的if大概能猜到有4條規則..你可以想更多.

void  genpattern(const

char* const des,int ** pattern, int*size )

else

j++;

} }

再提供乙個與之配套的kmp演算法 ,將**複製。自己跑跑,有視覺化的console列印

int kmp(const

char* src,const

char *des )

for (int a=0;a< j+1;a++)

printf("\n

");for (int a=0;a)

printf(

"%s\n\n

",des);

}if(*(p+i) != *(des+j))

else

if(pattern[j]==-1

)

else

}else

}return -1

;}

免費**乙個main函式 :)

---console------------

1

abcabcx2|

3abcx45

abcabcx6||

7abcx89

abcabcx

10|||

11abcx

1213

abcabcx

14|||*

15abcx

1617

abcabcx18|

19abcx

2021

abcabcx

22||

23abcx

2425

abcabcx

26|||

27abcx

2829

abcabcx

30||||

31 abcx

選擇排序的另一種思路

在我們上次的選擇迴圈中,我們用了乙個minindex變數來儲存最小值的下標,每次迴圈完畢後用最小值下標的元素與手元素進行交換,遍歷一次,記錄下最值元素所在位置,遍歷結束後,將此最值元素調整到合適的位置。但是我們還有種交換變數的方式,我們可以用待排序陣列中的元素與待排序序列中的首元素比較,如果比待排序...

另一種尊重

上中學的時候,有一節課印象非常深刻。老師問我們如果無意闖入乙個房間,發現房間裡有一位女士正在洗澡,這時應該怎麼辦?有同學回答就當什麼也沒看見,退出房間。還有同學回答 說聲對不起!女士。然後退出去。老師笑了笑說,還有更好的答案,那就是 對不起,先生!有一對結婚多年的夫妻,有一次出差在外的妻子有一件急事...

另一種勝利

另一種勝利 written by allen lee 剛才我的扣殺,出界了5.3厘公尺。雖然很可惜,但還是出界了,請確認下吧。幹 真是的,那些任性的傢伙!但是,到最後還只顧自己網球原則的正直笨蛋,和一定要用迴旋蛇標打中單人區的笨蛋,給我們看了場好比賽啊。龍崎 海棠和幹他們雖然輸了這場比賽,但他們堅持...