Manacher 算法-----o(n)迴文串算法

         迴文的含義是:正着看和倒着看相同,如abba和yyxyy

       Manacher算法基本要點:用一個非常巧妙的方式,將所有可能的奇數/偶數長度的迴文子串都轉換成了奇數長度:在每個字符的兩邊都插入一個特殊的符號。比如 abba 變成 #a#b#b#a#, aba變成 #a#b#a#。 爲了進一步減少編碼的複雜度,可以在字符串的開始加入另一個特殊字符,這樣就不用特殊處理越界問題,比如$#a#b#a#。

     Pid]:以字符strid]爲中心的最長迴文串,當以strid]爲第一個字符時,這個最長迴文串向右延伸了Pid]個字符。示例:

     id: 0  1  2  3  4  5  6  7  8  9  10  

  str[id]: #  a  #  b  #  a  #  c  #  b   #

   P[id]: 1  2  1  4  1  2  1  2  1  2  1  

  這裏有一個很好的性質,Pid-1就是該回文子串在原串中的長度。現在的關鍵問題就在於怎麼在On)時間複雜度內求出P數組了。只要把這個P數組求出來,最長迴文子串就可以直接掃一遍得出來了。

  由於這個算法是線性從前往後掃的。那麼當我們準備求Pi]的時候,i以前的Pj]我們是已經得到了的。我們用mx記在i之前的迴文串中,延伸至最右端的位置。同時用id這個變量記下取得這個最優mx時的id值。(注:爲了防止字符比較的時候越界,我在這個加了‘#’的字符串之前還加了另一個特殊字符‘$’,故我的新串下標是從1開始的)
好,到這裏,我們可以先貼一份代碼了。

	void getMaxPlalinStr(char[] s){
		int id = 0;  // 記錄當前最大回文串的中心位置
		int max = 0; // 記錄當前最大回文串的最右邊界
		int len = s.length; 
		int[] p = new int[len]; // 記錄以每個字符爲中心的迴文串長度
		
		// 線性遍歷,求解p[i]
		for(int i=0;i<len;i++){
			
			p[i] = max > i ? (p[2*id-i]<(max-i) ? p[2*id-i]:(max-i)) : 1;
			
			while(i-p[i]>=0 && i+p[i]<len && s[i+p[i]]==s[i-p[i]])
				p[i]++;
			if(p[i]+i > max){
				max = p[i]+i;
				id = i;
			}
		}
		max = 0;
		for(int i=0;i<len;i++){
			if(max < p[i]){
				max = p[i];
				id =i;
			}
			
		}
		System.out.println("id="+id+",max="+max);
	}


   代碼是不是很短啊,而且相當好寫。很方便吧,還記得我上面說的這個算法避免了很多不必要的重複匹配吧。這是什麼意思呢,其實這就是一句代碼。

p[i] = max > i ? (p[2*id-i]<(max-i) ? p[2*id-i]:(max-i)) : 1;



就是當前面比較的最遠長度mx>i的時候,Pi]有一個最小值。這個算法的核心思想就在這裏,爲什麼P數組滿足這樣一個性質呢?
   (下面的部分爲圖片形式)


   

    參考:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824



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