生活随笔
收集整理的這篇文章主要介紹了
KMP及其改进算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文主要講述KMP已經KMP的一種改進方法。若發現不正確的地方,歡迎交流指出,謝謝!
KMP算法的基本思想:
KMP的算法流程:
每當一趟匹配過程中出現字符比較不等時,不需回溯 i 指針,而是利用已經得到的部分匹配的結果將模式向右滑動盡可能遠的一段距離后,繼續進行比較。
設S為目標串 ,T為模式串 ,設 i 指針和 j 指針分別指示目標串和模式串中正待比較的字符。
開始時,令i=0,j=0。如果Si==Tj,則使i和j的值分別增加l;反之,i不變,j的值退回到j=next[j]的位置(即模式串右滑),然后再對Si和Tj進行比較。依次類推,直到出現下列兩種情況之一 :
1.j值退回到某個j=next[j]時,有 Si==Tj ,則指針的值各增加1后,再繼續匹配;
2.j值退回到 j=-1,此時令指針的值各增加1,也即下一次對Si+1和T0進行比較。
模式匹配KMP算法的時間復雜度為O(m+n), 只有當模式與珠串之間存在許多“部分匹配”的情況下顯得比樸素字符匹配算法快得多。但是KMP算法最大的特點就是指示主串的指針不需要回溯,整個過程中,對主串僅需從頭至尾掃描一遍,這對處理從外設輸入的龐大文件很有效,可以邊讀入邊匹配,而無需回頭重讀。
?
跟樸素匹配算法的主要差異:
???在于當Si != Tj的時候,樸素算法采用的是將Tj往前推一格,然后將j置為0,重新進行匹配,而KMP采用的方法是將j置為next[j],然后再匹配。
很顯然,這里的next[j]是算法的核心 。
?
下面是next[j]的計算方法,以及代碼的實現:
[cpp] ?view plaincopy
void ?get_nextval(? const ? char ?*s,? int ?*nextval)?? {?? ?????int ?len?=?strlen(s);?????? ?????int ?i?=?0,?j?=?-1;?? ?? ?????nextval[0]?=?-1;?????? ?? ?????while (?i?<?len-1?){?? ??????????if (?j?==?-1?||?s[i]?==?s[j]?){?? ???????????????++i;?????? ???????????????++j;?????? ?? ???????????????if (?s[i]?!=?s[j]?){?? ????????????????????nextval[i]?=?j;?????? ???????????????}else {?? ????????????????????nextval[i]?=?nextval[j];?????? ???????????????}?? ??????????}else {?? ???????????????j?=?nextval[j];?????? ??????????}?? ?????}?? ?? ?????return ?;?????? }??
?
得到了next[j]之后,KMP算法的實現就很簡單了,按照上面KMP的算法流程,可以很快寫出代碼:
[cpp] ?view plaincopy
?? ?? int ?kmp(? const ? char ?*s,? const ? char ?*t?)?? {?? ?????int ?k?=?-1;?????? ?????int ?nextval[N]?=?{0};?????? ?????int ?s_len?=?strlen(s);?????? ?????int ?t_len?=?strlen(t);?????? ?? ?????get_nextval(?s,?nextval?);??????? ?????cout<<"nextval:" <<endl;?????? ?????for (?k?=?0;?k?<?s_len;?k++)?? ??????????cout<<nextval[k]<<"?" ;?????? ?????cout<<endl;?????? ?? ?????int ?i?=?0,?j?=?0;?????? ?????? ?? ?????while (?i?<?t_len?&&?j?<?s_len?){?? ??????????if (?j?==?-1?||?t[i]?==?s[j]?){?? ???????????????i++;?????? ???????????????j++;?????? ??????????}else {?? ???????????????j?=?nextval[j];?????? ??????????}?? ?????}?? ?? ?? ?????if (?j?>=?s_len?){?? ??????????return ?i-s_len;?????? ?????}else {?? ??????????return ?-1;?????? ?????}?? }??
?
下面給出一個KMP的實現及測試代碼:
[cpp] ?view plaincopy
#include?<iostream> ?? using ? namespace ?std;?????? #define?N?100 ?? ?? void ?get_nextval(? const ? char ?*s,? int ?*nextval);?????? int ?kmp(? const ? char ?*s,? const ? char ?*t?);?????? ?? ?? ?? ?? int ?kmp(? const ? char ?*s,? const ? char ?*t?)?? {?? ?????int ?k?=?-1;?????? ?????int ?nextval[N]?=?{0};?????? ?????int ?s_len?=?strlen(s);?????? ?????int ?t_len?=?strlen(t);?????? ?? ?????get_nextval(?s,?nextval?);??????? ?????cout<<"nextval:" <<endl;?????? ?????for (?k?=?0;?k?<?s_len;?k++)?? ??????????cout<<nextval[k]<<"?" ;?????? ?????cout<<endl;?????? ?? ?????int ?i?=?0,?j?=?0;?????? ?????? ?? ?????while (?i?<?t_len?&&?j?<?s_len?){?? ??????????if (?j?==?-1?||?t[i]?==?s[j]?){?? ???????????????i++;?????? ???????????????j++;?????? ??????????}else {?? ???????????????j?=?nextval[j];?????? ??????????}?? ?????}?? ?? ?? ?????if (?j?>=?s_len?){?? ??????????return ?i-s_len;?????? ?????}else {?? ??????????return ?-1;?????? ?????}?? }?? ?? void ?get_nextval(? const ? char ?*s,? int ?*nextval)?? {?? ?????int ?len?=?strlen(s);?????? ?????int ?i?=?0,?j?=?-1;?? ?? ?????nextval[0]?=?-1;?????? ?? ?????while (?i?<?len-1?){?? ??????????if (?j?==?-1?||?s[i]?==?s[j]?){?? ???????????????++i;?????? ???????????????++j;?????? ?? ???????????????if (?s[i]?!=?s[j]?){?? ????????????????????nextval[i]?=?j;?????? ???????????????}else {?? ????????????????????nextval[i]?=?nextval[j];?????? ???????????????}?? ??????????}else {?? ???????????????j?=?nextval[j];?????? ??????????}?? ?????}?? ?? ?????return ?;?????? }?? ?? int ?main()?? {?? ?????char ?s[N],?t[N];?????? ?????while (?cin>>s?>>t?){?? ??????????int ?i?=?0;?????? ??????????i?=?kmp(?s,?t?);?????? ?? ??????????cout?<<"ans?=?" ?<<i?<<endl;?????? ?????}?? ?????return ?0;?????? }??
測試如下:
KMP模式匹配問題的改進思想和方法
KMP的不足之處:
??? 通過觀察,我們可以在原有的KMP算法中,發現一個不 足的地方 ,也就是我們將要改進的地方就是,因為,子串的出現是隨機的,如果子串在主串出現的位置靠后的時候,KMP 算法實在顯得比較低效 。現在我們給出一個例子來說明問題。
?
主串為:aspowqeursoolksnkhiozbgwoinpweuirabaac
子串為:abaac
?
??? 容易看出,子串要到最后才會得到匹配,因此,我們提出我們的思想——從主串的首和尾同時進行匹配,那樣,就可以提高算法的效率,并且,除了在這個方面提高算法效率以外,我們還想到,當 m >>n,,n>>0的時候(m為主串長度,n為子串長度),并且子串并沒有在主串中出現的話,那么,在改進算法中,我們將不需要比較到最末才判斷是否存在匹配的子串,而是通過剩下的字符數,來判斷是否存在足夠的字符與子串匹配,如果不足的話,那樣就不存在,否則就繼續匹配下去。
?
如何實現從主串末尾想串頭開始匹配呢?
??? 我們這里有兩個方案:第一個方案 是,把子串逆轉,然后沿用舊的KMP算法中的next函數求出其逆轉后的子串的next值,再用以進行匹配;第二個方案 就是,不需要把子串逆轉,而是采用一個新的next函數直接求出其逆轉后的next值。
??? 第一二個方案比較后,我們選擇第二個方案。因為,在 n>>0的時候,明顯地,在把子串逆轉的時候同時需要多一個字符串來存放,并且,在不同的匹配都需要一個新的字符串,這樣就大大地浪費空間了,除此之外,第一個方案至少要做遍歷子串兩次,而第二個方案只需要遍歷子串一次就可以了。所以我們決定采用構建一個新的next函數來求出其逆轉后的子串next值。
??? 我們新的next函數的思想就是,把末字符看成是首字符,然后,仿照KMP算法中的next函數的實現方式最終實現的。現在,我們給出實現的新的next函數:
[cpp] ?view plaincopy
void ?nextres(? char *?p,? int ?*next,? int ?n?)?? {?? ????????int ?i,?j,?k;?????? ????????i?=?n-1,?j?=?-1;?????????? ????????*next?=?-1;??????? ????????k?=?n;???? ????????while (?i?>?0?){?? ????????????????if (?j?==?-1?||?*(p+i)?==?*(p+k-j-1)){?? ????????????????????????i--,?j++;????????? ????????????????????????if (?*(p+i)?!=?*(p+k-j-1)?)?? ????????????????????????????????*(next+n-i-1)?=?j;???????? ????????????????????????else ?? ????????????????????????????????*(next+n-i-1)?=?*(next+j);???????? ????????????????}?? ????????????????else ??? ????????????????????????j?=?*(next+j);???? ????????}?? ?? }??
在得到逆轉后的子串的next函數后,我們就可以進行串的匹配了。其基本思路同原KMP算法,下面我們就給出匹配過程的實現:
[cpp] ?view plaincopy
int ?march(? char *?mainhead,? char *?head,? int ?mainlen,? int ?lenth,? int ?*next1,? int ?*next2?)?? {?? ????????int ?i,?j,?k,?l,?m;???????? ????????i?=?0,?j?=?0,?k?=?mainlen-1,?m?=?lenth-1,?l?=?0;?????????? ????????while (?(m>0?&&?j<lenth)?||?lenth?==?1?){?? ????????????????if (?lenth?==?1?&&?(?*(mainhead+i)?==?*(head+j)?||?? ????????????????????????*(mainhead+k)?==?*(head+m)))?? ????????????????????????return ?1;????????? ????????????????if (?j?==?-1?||?*(mainhead+i)?==?*(head+j))?? ????????????????????????i++,?j++;????????? ????????????????else ?? ????????????????????????j?=?*(next1+j);??? ????????????????if (?l?==?-1?||?*(mainhead+k)?==?*(head+m)){?? ????????????????????????k--;?? ????????????????????????l++;?? ????????????????????????m?=?lenth==2?m-l:m-1;????? ????????????????}else {?? ????????????????????????l?=?*(next2+1);??? ????????????????????????if (?l?!=?-1?)?? ????????????????????????????????m?=?m-l;?????????? ????????????????????????else ?? ????????????????????????????????m?=?lenth-1;?????? ????????????????}?? ????????????????if (?k-i?<?m-j?)?? ????????????????????????return ?0;????????? ????????}?? ?? ????????if (?m?<=?0?||?j?>=?lenth)?? ????????????????return ?1;????????? ????????else ??? ????????????????return ?0;????????? }??
新的KMP算法在某種程度上的確可以提高模式匹配的效率。除此以外,新的模式匹配算法還能提早結束不必要的匹配 。
from:?http://blog.csdn.net/cyh_24/article/details/8162436
總結
以上是生活随笔 為你收集整理的KMP及其改进算法 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。