終於接觸了一丁點字元匹配的東西(其實也只是入門的KMP),話說這個東西的確需要很強的數學思維,其中NEXT數組尤其坑爹,很多ACMER就是栽在了這個數組上,不明覺厲。什麼諸如“失配” “數組移位”和一些S(i-1)...之類的一長串字母的確讓我們十分不爽。更有部落格表示這個確實很難,此中有真意,欲辨已忘言云雲。。演算法導論上的模板是從1開始的,自己還寫了個SString資料類型讓好多人云裡霧裡,今天我們就來試著解釋一下KMP的基本原理吧。
字元匹配的基本問題是尋找子串在一長串中的位置。比如
一長串s:abacababt
子串t:abab,傳回值就是4.當然你要非說5也行。。
尋找這種東西的位置,最先想到的思路自然是暴搜。。我們用i來表示大串中當前尋找的位置,j表示子串中當前尋找的位置,開始都是0,然後開搜,條件很簡單,s[i]==t[j]。。。
原理見圖,
代碼如下:
//KMP的前奏——BF //O(n)=m*n #include <iostream>#include <string>using namespace std;int BF_Find(string s,string t,int pos) //pos從0開始,輸出絕對位置要加1 {int i=pos;int j=0;while(i<s.size() && j<t.size()){if(s[i]==t[j]){i++;j++;}else{i=i-j+1;//一個一個的搜j=0;}}if(j>=t.size()) //目標串匹配結束 {return i-t.size();}elsereturn -1;}int main(){string need,tar;int pos;cin>>need>>tar>>pos;cout<<BF_Find(need,tar,pos)+1<<endl;//輸出絕對位置 return 0;}
喜聞樂見的是,這個方法時間複雜度高,達o(M*N),一會會就TLE了,於是KMP正式登場,它的思想是預先處理,解決了每匹配失敗一次,I就無腦後移J清零的問題。(時間主要就浪費在這上邊了)預先處理的方式就是這個坑爹的數組——next[]。
網上的說法好多都是用的數學推理,當時就ORZ了。。
next數組的本質其實就是下!!簡要介紹下。
next[n],其實就是求從前往後數的字串和從後往前數的字串完全對應的最長位元(從後數和從前數不能完全相同),舉例如a[5]=ababbc,
next[0]=0(也可以是-1,網上的教程表示無所謂)
next[1]=0,這個串(ab)從首碼數有a,ab,從尾碼數有b,ab……不能相同,所以沒一樣的,為0.
next[2]=1,這個串(aba)從首碼數有a,ab,aba,從尾碼數有a,ba,aba……不能相同(去掉aba和aba),還剩下a[0]即a與a[3]即a完全對應,為1.
next[3]=2,這個串(abab)從首碼數有a,ab,aba,abab從尾碼數有b,ab,bab,abab……不能相同(去掉abab和abab),還剩下ab和ab完全對應,為2.
next[4]=0,這個串(abab)從首碼數有a,ab,aba,abab,ababb從尾碼數有b,bb,abb,babb,ababb……不能相同(去掉ababb和ababb),沒一樣的,為0.
next[5]=0,原理同上。
這個數組可以使用搜尋的方法得到。網上的教程品質參差不齊,這裡給出一個能用的。下標從0開始,next[0]預設為0,獲得的數組可以使用調試功能查看。
int next[1000];void build_next(string b){ int i,j=0,key=0; memset(next,0,sizeof(next)); for(i=1;i<b.size();i++) { j=next[i-1]; while(j==1 && b[i]!=b[j]) { j=next[j-1]; } if(b[j]==b[i]) { next[i]=j+1; } else next[i]=0; }}
這個數組的理解與否是掌握KMP的關鍵,具體怎麼使用,下篇文章會給出更新。