生活随笔
收集整理的這篇文章主要介紹了
高效KMP字符匹配算法就这么简单
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、聊一聊
上一篇文章
"暴力"字符匹配算法的C語言實現
2、KMP算法介紹
1
KMP介紹
KMP是一種字符匹配算法,為啥叫KMP呢?因為是由D.E.Knuth,J.H.Morris和V.R.Pratt大佬提出來的。那一些小伙伴會問了怎么不叫"DJV算法"呢?因為老外都是名字在前,姓氏在后。
說實在的bug菌初次接觸這個字符匹配算法的時候也是一臉懵逼,那時候也是找了很多的資料來理解、分析和推導等等,很多知識回頭一看也就那么回事,想不明白那時候怎么會覺得那么復雜,或許這就是對未知事物的一種敬畏吧。
KMP算法說到底和暴力字符匹配功能上是一模一樣的,就是查找匹配字符串在主字符串中的位置,如果只是應用完全可以不用理會它是怎么實現的,調用幾個函數->傳遞幾個參數->得到結果,然后記得他比暴力匹配高效一點就行了。好了,本文看來要收尾了。
2
還不能結束
其實我們學習理論知識并不是學習知識本身,而是要學習了以后能夠獲得一種解決問題的辦法和思路。前面那篇文章為大家講解了暴力匹配,也跟大家在文中留了一個問題,假如沒有KMP算法我們是否有思路去更好的優化匹配效率呢?等思考完后再來看看KMP是如何處理該問題的。
KMP算法的核心 : 就是盡可能利用更多匹配過程中的信息來減少匹配串與主串的匹配次數從而提高匹配效率。
3、原理分析
1
幾個實例分析
那肯定得把暴力匹配中需要優化的匹配過程拿過來,如下圖所示:
匹配完abab以后匹配失敗,按照暴力匹配會把模式串移動一個字節繼續重頭開始匹配,然而再次匹配必定不成功,得繼續把模式串移動一個字節進行匹配。
前面我們講到KMP算法利用已經進行過匹配過的信息進行優化匹配,如上圖當進行第一次匹配以后,我們能夠利用的信息有兩個:1)模式串信息;2)已經匹配過的ababa信息,其他信息暫且還未知。
那么就簡單點處理 : 如果已經匹配的字符串前兩個字節與后面兩個字節相等,那么就把模式串移動兩個位,繼續跟主串比較即可,因此從第三個字符開始進行接下來的匹配,無需重頭開始匹配,最終匹配成功。
? ??上圖是最簡單的情形,那么下面我們看另外一種情況:
按照之前的做法,如果已經匹配過的aaaa中前兩個字節等于后兩個字節,那么模式串應該移動兩個字節,然而我們發現其只需要移動一個字節就能匹配成功了,看來僅僅用之前的策略還不夠完善,還得補充其它策略才行。
2
總結規律獲得算法
通過前面兩個例子了解到,我們只需要通過一套統一的規律來指導第A個字符匹配不成功以后下一步該對第B個字符進行匹配即可。于是就形成一張映射表-next數組表(不用太糾結名字,主要的意思是下一步該如何動作所以叫next)。
這個next數組表是通過模式串獲得的,首先給大家看看一個next表:
分析一下:
4、KMP算法的C實現
1
實現代碼
對于KMP算法的代碼實現真的非常巧妙,而且特別的簡短,如下是KMP算法的實現,KMP算法目前也存在比較多的改進版本,大家常用的還是如下實現:
1#include?<stdio.h>2#include?<stdlib.h>3#include?<string.h>45#define?NEXT_LEN?667char?*Parent?=?"1234567891212123456789";8char?*Chind??=?"121212";?9int??next[NEXT_LEN]?=?{0};1011/******************************************************12?* Fuction:KMP匹配算法查詢函數?13?* Params : str1:主串;str2:模式串;next:next數組14?*?????????(公眾號?:?最后一個bug)?15?*****************************************************/16char*?KMP(const?char?*?str1,?const?char?*?str2,int?*?next)?17{18????int?i?=?0;?19????int?j?=?0;20????char?*?ret?=?(char*)str1;2122????while?(i?<?strlen(str1)?&&?j?<?(int)strlen(str2))?//主串結束、模式串成功?23????{24????????//j?=?-1直接到next[0]處理或者字符匹配成功25????????if?((j?==?-1)||(str1[i]?==?str2[j]))?26????????{27????????????//下一個字符移動?28????????????i++;?29????????????j++;30????????}31????????else?32????????{33????????????//如果匹配不成功通過j(模式串比較失敗地址)找到next中下一次與主串比較的模式串地址?34????????????j?=?next[j];?35????????}????36????}3738????if?(j?==?strlen(str2))//表示的是模式串全部匹配?39????{40???????return?(ret?+?i?-?j);41????}42????else?43???????return(NULL);44}45/******************************************************46?* Fuction:KMP匹配算法next數組生成?47?* Params : str:模式串;next:next數組48?*?????????(公眾號?:?最后一個bug)?49?*****************************************************/50void?getNext(const?char?*?str,?int?*?next)51{52????next[0]?=?-1;53????int?i?=?0,?j?=?-1;5455????while?(i?<?(strlen(str)?-?1))56????{57????????if?((j?==?-1?)||(str[i]?==?str[j]))//通過模式串自身對比獲得next數組值?58????????{59????????????++i;60????????????++j;61????????????next[i]?=?j;62????????}???63????????else64????????{?65????????????j?=?next[j];66????????}67????}68}6970/******************************************************71?* Fuction:暴力匹配算法?72?* Params :str1:主串;str2:模式串?73?*????????(公眾號?:?最后一個bug)?74?*****************************************************/75char?*strstr(const?char?*str1,?const?char?*str2)76{77????char?*cp?=?(char*)str1;78????char?*s1,?*s2;7980????if?(!*str2)81????????return((char?*)str1);8283????while?(*cp)84????{85????????s1?=?cp;86????????s2?=?(char?*)str2;87????????while?(*s1?&&?*s2?&&?!(*s1?-?*s2))88????????{89????????????s1++,?s2++;90????????}??9192????????if?(!*s2)93????????????return(cp);9495????????cp++;96????}97????return(NULL);98}99
100/******************************************************
101?* Fuction:對比主函數?
102?*?Author?:?(公眾號?:?最后一個bug)?
103?*****************************************************/
104int?main(int?argc,?char?*argv[])?{
105????int?result?=?0;
106????int?i?=?0;
107????//獲得KMP的next數組?
108????getNext(Chind,next);
109????for(?i?=?0;?i?<?NEXT_LEN;i++)
110????{
111??????printf("Next[%d]?=?%d\n",i,next[i]);??
112????}
113????//進行KMP匹配?
114????result?=?KMP(Parent,Chind,next)-?Parent;
115????printf("KMP????result?=?%d\n",result);
116????//進行暴力匹配?
117????result?=?strstr(Parent,Chind)?-?Parent;
118????printf("strstr?result?=?%d\n",result);
119
120????return?0;
121}
運行結果如下:
分析一下:
大家仔細閱讀了會發現,其實求next數組和KMP匹配算法是非常的相似,KMP算法的關鍵就是求next數組,一旦匹配不成功就會去next數組中找下一個模式串的匹配點,再次拿著該匹配點與匹配失敗主串進行比較.
所以KMP算法的優越之處在于不再需要重頭開始比較,其主串的比較指針是不會倒退的。至于兩個算法的時間上的復雜度KMP<strstr,這一部分的推導bug菌就不深入分析了,大家有時間可以查閱一下相關資料進行進一步閱讀和學習,具體的應用bug菌在前面的暴力匹配有跟大家講解過,這里就不在贅述了。
5、最后小結
KMP算法就跟大家介紹到這里了,希望大家能有新的收獲,算法的話大家也不需要死記硬背,基本到處都能夠找到,了解原理并且能夠獲得這種處理問題的思維就達到學習的目的了。
?
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
關注公眾號,后臺回復「1024」獲取學習資料網盤鏈接。
歡迎點贊,關注,轉發,在看,您的每一次鼓勵,我都將銘記于心~
總結
以上是生活随笔為你收集整理的高效KMP字符匹配算法就这么简单的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。