KMP算法初始化模式串的next數組

在使用KMP算法處理字符串查找問題的過程當中,可以利用模式串本身的對稱性,在移動模式串的時候,儘量多的往後移動,減少無用的查找過程,而模式串本身的對稱性一般是保存在一個next數組裏面的,下面來討論下怎麼初始化next數組的值。

先來看一下下面這個例子:
示例
申明一下:下面說的對稱不是中心對稱,而是中心字符塊對稱,比如不是abccba,而是abcabc這種對稱。

分析:
i=0:模式串爲m,最長前綴子串和後綴子串都爲空,next[0] = 0;
i=1:模式串爲mb,最長前綴子串爲m,最長後綴子串爲b,無對稱塊,next[1] = 0;
i=2,i=3:同理,next[2] = 0,next[3] = 0;
i=4:模式串爲mbwtm,最長前綴子串爲mbwt,最長後綴子串爲bwtm,對稱塊爲m,長度爲1,next[4] = 1;
i=5:模式串爲mbwtmb,最長前綴子串爲mbwtm,最長後綴子串爲bwtmb,對稱塊爲mb,長度爲2,next[5] = 2;
i=6:模式串爲mbwtmbw,最長前綴子串爲mbwtmb,最長後綴子串爲bwtmbw,對稱塊爲mbw,長度爲3,next[6] = 3;

i=13:模式串爲mbwtmbwmbwtmbw,最長前綴子串爲mbwtmbwmbwtmb,最長後綴子串爲bwtmbwmbwtmbw,對稱塊爲mbwtmbw,長度爲7,next[13] = 7;
i=14:模式串爲mbwtmbwmbwtmbwt,最長前綴子串爲mbwtmbwmbwtmbw,最長後綴子串爲bwtmbwmbwtmbwt,對稱塊爲mbwt,長度爲4,next[14] = 4;
i=15:模式串爲mbwtmbwmbwtmbwtb,最長前綴子串爲mbwtmbwmbwtmbwt,最長後綴子串爲bwtmbwmbwtmbwtb,無對稱塊,next[15] = 0;

根據上面的例子,可以總結一下下面的規律,後面出現模串的位置用P替代:

  1. 當前字符的前一個字符的對稱塊長度爲0(next數組對應位置的值爲0)時,只需要比較當前字符跟模式串的第1個字符P[0],若相等則當前位置對應的next數組值爲1,否則爲0;
  2. 按照規律1,如果當前字符的前1個字符的對稱塊長度爲1,此時只需要比較當前字符跟模式串的第2個字符P[1],若相等則當前位置對應的next數組值爲2。如果當前字符的前1個字符的對稱塊長度爲2,此時只需要比較當前字符跟模式串的第3個字符P[2],若相等則當前位置對應的next數組值爲3。例如當前位置i=6時,可以知道i=5時,next[5] = 2,此時只需要比較模式串位置i=6的字符w跟模式串位置i=next[5]=2位置的字符P[2]=w是否相等(前面的兩個字符mb在i=5時已經比較過且相等了),相等話next[6] = next[5] + 1 = 3;
  3. 按照規律2,如果一直想等的話,那就可以一直累加,但是總會出現不相等的時候,如果出現了不相等的情況,並不是說完全沒有對稱塊了,只是對稱塊的長度變短了,需要重新計算。

針對上面規律3出現的不相等的時候,怎麼計算此時next數組對應位置的值,我們以上面例子的i=14爲例子來說明:

  1. next[13] = 7,表示模式串的i=0到i=6這一串跟i=7到i=13這一串是完全相等的,設置K=7,K表示前一位置的最長對稱塊的長度。
  2. i=14時,由於P[14] = t != m = P[7]不相等,所以可以肯定的是i=14位置的next[14]的值肯定比next[13]小,且只能在i=0到i<7這一區間內尋找了.
  3. 因爲P[14] != P[7],所以P[0-6]這一段肯定不是對稱塊了。那麼新的對稱塊是從i=0到i等於多少的位置呢?可以知道P[0-6] == P[7-13],所以可以先計算P[0-6]的對稱塊長度,即next[K-1]的值。結果是mbw,即P[0-2] == P[4-6],從P[0-6] == P[7-13]可以得到P[4-6] = P[11-13],所以[0-2] == P[11-13],然後比較P[14]是否等於P[3]。
  4. 如果相等,則next[14] = next[K-1] + 1 = next[7-1] + 1 = 4。(如果不相等,則重複步驟3和4。假設P[14] = x != t = P[3],則k = next[k-1] = 3,重複第3步,從P[0-2]裏面尋找最長的對稱塊,此時next的值都爲0,所以此時next[14] = 0)。

下面列一下代碼並配合註釋,幫助理解。

void initNext(const char P[],int next[]) { 
	int i;						//i:模式串下標
    int k;						//k:最大對稱塊長度 
    int pLen = strlen(P);		//pLen:模式串的長度 
    next[0] = 0;					//模式串的第一個字符的對稱塊長度爲0 

	//for循環,從第二個字符開始,依次計算模式串每一個字符對應的next數組值 
    for (i = 1,k = 0; i < pLen ; i++){
    	//遞歸的求出P[0]···P[i]的最大的相同的前後綴長度k 
		while(k > 0 && P[i] != P[k]) {
			//當P[i] != P[k]時,參考上面例子中的第三步,需要循環計算k的值
			k = next[k-1];   
		}       
		//如果相等,那麼最大相同前後綴長度加1(參考上面說的規律2)
       	if (P[i] == P[k]){
 			k++;
		}
		//賦值k到next數組的i位置
		next[i] = k;
	}
}

注意一下,next 數組考慮的是除當前字符外的最長相同對稱快,所以通過上述方法求得的各個位置的最大相同對稱塊數組next之後,只要稍作變形即可:將next數組中的元素整理往後移動一位,且next[0] = -1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章