1. 程式人生 > >拓展KMP演算法詳解

拓展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的位置
        }
    }
}