【总结】字符串匹配: KMP 和 拓展KMP
http://chaoswork.com/blog/2011/06/14/kmp%E7%AE%97%E6%B3%95%E5%B0%8F%E7%BB%93/
kmp模板 next[0]=-1;j=-1; for(i=0;i<m;) {while(j>=0 && p[i]!=p[j])j=next[j];i++;j++;next[i]=j; }for(i=0,flag=0,j=0;i<n;) {while(j>=0 && p[j]!=t[i])j=next[j];i++;j++;//已經匹配了模式串的多少個if(j==m)//匹配成功 }接下來需要更加深入地了解next數組,許多題目需要用到它的定義來預處理字符串:
?xdu 1154
顯然,用2次kmp處理處前綴和后綴在各點的匹配情況,用dp記錄符合要求的子串,有幾個要注意的地方:(調了2個小時T_T)
1.前綴的其實位置不但要在后綴的前面,終止位置不能超過后綴的終止位置,也就是說前綴不能包含后綴.
2.
View Code for(i=0,flag=0,j=0;i<n;) {while(j>=0 && p[j]!=t[i])j=next[j];i++;j++; if(j==m)//若在此處記錄則會出現bug,因為此時匹配完成點是i-1 }CF也有一道與上面類似的題,需要用kmp預處理最左端前綴和最右端后綴
http://codeforces.com/contest/149/problem/E
循環節問題
它還能用來求周期字符串的循環節HDU1358:
性質:
當且僅當len%(len-next[len])==0時,str[next[len]~len-1]為最小循環節
要證明它需要說明3點:
1.一個字符串str是周期串,假設s1為它的循環節,則str=s1 s1...s1(n個)?
推導出=>len%(len-next[len])==0成立.
2.next[len]~len-1為s1 ,len%(len-next[len])==0時?
推導出=>str為周期串,s1為最小循環節
3.如何保證是最小的.
證明:
1.由next的性質知道,s[1~next[len]-1]與s[1~len-1]有最長的相等的前綴和后綴s,很顯然s就是n-1個s1了.
2.設s1的長度為L,由于len%L==0 , str可以分解成若干個長度為L的小串,設它們從左到右依次為
a1?, a2?... an
根據匹配關系得
a1=a2;
a2=a3;
..
an-1=an;
因此a1=a2=a3...=an;
3.next[len]保證了前綴與后綴最大化,如果循環節s1存在而s1內還有循環節s1',則next[len]可以向后移動,與定義矛盾.
?
相等的循環同構問題
hdu3374 string problem
分析:
最大和最小的循環同構字符串可以分開處理,求位置可以利用最小最大表示法(03wc論文 周源)
求出現的次數可以用kmp掃一遍,但利用 循環節 可以更快地得到答案.
性質:
字符串str由k個最小循環節s1組成,則它的相等循環同構數為k.
證明:
設相等循環同構數為p,我們可以利用循環節s1構造出k個相等的循環同構,于是p>=k;
下面證明p<=k:
如果p>k
假設在移動完s1到尾部前,出現了相同的循環同構串,不妨設此時移動的串為s2,
則利用 循環節性質2第1種情況的推理方法 可以知道s2為字符串的一個循環節
且s2的長度<s1,與s1為最小循環節的條件矛盾,因此p<=k.
于是p只能等于k.
?
拓展KMP
有很長一段時間單純地以為拓展kmp只是kmp倒過來跑,后來發現很多問題其實無法轉換成kmp解決,于是怒學了一下拓展kmp.
首先比較一下kmp和拓展kmp解決的問題:
kmp解決了求所有主串的前綴pre[i]?(0<=i<n)的后綴與模式串前綴的最大匹配長度問題;
拓展kmp解決了所有主串的后綴suf[i]的前綴,與模式串前綴的最大匹配長度問題.
1 void getNext() { 2 int l = 1, r = -1, i, j; 3 4 for (next[0] = 0, i = 1; p[i]; ++i) { 5 if (i + next[i - l] - 1 < r) next[i] = next[i - l]; 6 else { 7 for (j = max(r - i + 1, 0); p[i + j] && p[i + j] == p[j]; ++j); 8 next[i] = j; l = i; r = i + j - 1; 9 } 10 } 11 next[0] = i; 12 }
?
設模式串為str;
定義next[i]為 str 與 它的后綴suf[i]的最大公共前綴長度.
r是當前已經確定匹配區間的最右端點,l是對應的左端點,即?r=l+next[l]-1;
當要求next[i]時
根據 next定義 str[ l , l+next[l]-1 ] == str[ 0 , next[l]-1 ];
得到 str[ i , l+next[l]-1 ] == str[ i-l , next[l]-1 ];
設s1=str[ i , l+next[l]-1 ];?
討論以下情況:
1. 若 ?i在 [l,r] 區間內 ?
next[i-l]的值我們已經知道,這時候需要討論:
如果 ?next[i-l] 小于 s1 的長度,那么可以知道在下標為 next[i-l] 的位置必定會失配,于是next[i]=next[i-l];
如果 ?next[i-l] 大于或等于 s1 的長度,那么直到r位置,我們都可以確定已經匹配上了,接下來需要確定r后面
位置的匹配情況,而此時i已經匹配了r-i+1的長度,next[i]從這個值開始計數就可以了,計數完成后i+next[i]-1
已經大于r,因此要更新 ? r=i+next[i]-1 , l=i ;
2.若 i不在[l,r]的區間內,即 i > r, 前面得到的信息無法用到,于是我們需要從頭將str[i]與str[0]進行匹配,當然也要記得更新l,r.
? ?復雜度:
2個循環變量i,j都是單調增的,而他們最多增加n次,因此 復雜度是線性的.
拓展kmp求循環節的方法參考kmp求循環節部分.
知道這些后可以來解決這個問題
總結
以上是生活随笔為你收集整理的【总结】字符串匹配: KMP 和 拓展KMP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 懒虫小鑫
- 下一篇: sdut 双向队列(STL)