拓展KMP演算法詳解
演算法描述:設字串T,長度為n,字串S,長度為m。線上性時間內求出T的每一個字尾所對應S的最長字首。
假設T=“AAAAB”,S="AAAA"。
我們從第一位開始匹配,匹配結果存放到ext陣列中。顯然ext【0】=4,然後我們就去計算ext【1】,顯然ext【1】=3。如果還是如同算ext【0】這樣的方法去匹配ext【1】會造成資訊的浪費,因為前面有的資訊已經知道了,就不需要一個一個再去比對了。這裡我們記S【1到lens-1的元素】為S1,發現S1與S是有公共字首的,同樣我們用next陣列記錄下這公共字首,顯然next【1】=3。所以從T【1】開始之後的next【1】(3)位的數字都是相同的,這樣就不用從頭開始比對。
下面用圖來舉一個新的栗子。
我用黑色方格代表已經匹配的元素,黑色單向箭頭代表元素相等。紅色箭頭代表待匹配元素。
當我們匹配完第一個元素(下標0)的時候開始匹配第二個元素(下標1),發現ext【1】=2,這樣的話可以直接從S串的2號元素開始匹配,原因正如之前所說。
基本原理就是這個樣子。下面說一說具體如何實現拓展kmp演算法
首先我們要引入兩個新的變數,p,p0。表示p從p0開始最長的匹配位置(如果感到模糊請繼續看下去)。與manacher類似,我們需要維護這個最長長度。下面通過幾個圖來講解一下。
(1)如圖所示,紅色箭頭代表p,藍色箭頭代表p0,綠色箭頭代表待匹配位置,設next【綠色箭頭】=3。然後就有了下面的匹配方式。由於前三個相同,我們直接匹配第四個,也就是綠色方格。但是綠色方格中的元素可能相等嗎?答案是否定的。如果相等了,我們的next【綠色箭頭】就不是3了,而是4。雖然方格中沒有真正的元素,但是仍然可以推的出綠色方格中的元素是不可能相同的。
綠色部分如果匹配了,那麼next【綠色箭頭】就產生了矛盾,S中4,5號元素就會相等
這是綠色箭頭指向的元素匹配位置小於藍色箭頭(p0)的情況,那麼大於等於的情況會是怎麼樣呢?
(2)緊接著上一個栗子,如果next【綠色箭頭】=4,綠色箭頭+next【綠色箭頭】剛剛好指向了藍色箭頭的位置,這時候應該向下一位置開始匹配,因為還沒有搜尋到那個位置,所以你並不知道是不是會匹配,如果匹配了,那麼就更新p與p0,如此反覆,ext陣列就求解完畢,演算法結束。
.
然後我去抄了一個模板
const int maxn=10086; //字串長度最大值 int next[maxn],ex[maxn]; //ex陣列即為extend陣列 //預處理計算next陣列 void GETNEXT(char *str) { int i=0,j,po,len=strlen(str); next[0]=len;//初始化next[0] while(str[i]==str[i+1]&&i+1<len)//計算next[1] i++; next[1]=i; po=1;//初始化po的位置 for(i=2; i<len; i++) { if(next[i-po]+i<next[po]+po)//第一種情況,可以直接得到next[i]的值 next[i]=next[i-po]; else//第二種情況,要繼續匹配才能得到next[i]的值 { j=next[po]+po-i; if(j<0)j=0;//如果i>po+next[po],則要從頭開始匹配 while(i+j<len&&str[j]==str[j+i])//計算next[i] j++; next[i]=j; po=i;//更新po的位置 } } } //計算extend陣列 void EXKMP(char *s1,char *s2) { int i=0,j,po,len=strlen(s1),l2=strlen(s2); GETNEXT(s2);//計運算元串的next陣列 while(s1[i]==s2[i]&&i<l2&&i<len)//計算ex[0] i++; ex[0]=i; po=0;//初始化po的位置 for(i=1; i<len; i++) { if(next[i-po]+i<ex[po]+po)//第一種情況,直接可以得到ex[i]的值 ex[i]=next[i-po]; else//第二種情況,要繼續匹配才能得到ex[i]的值 { j=ex[po]+po-i; if(j<0)j=0;//如果i>ex[po]+po則要從頭開始匹配 while(i+j<len&&j<l2&&s1[j+i]==s2[j])//計算ex[i] j++; ex[i]=j; po=i;//更新po的位置 } } }