史上最清晰--O(n)時間求字符串的最長迴文子串解讀

    真的很煩有些人,想寫博客麼,又很差好寫,最起碼本身好好看一遍,糾糾錯,寫寫感悟,只顧本身看懂而不加以探討,那你寫博客還有什麼意義呢?更況且,看不看的懂還兩說,接下來就爲你們解釋一下網上各類搜索排名靠前的關於O(n)時間求字符串的最長迴文子串的算法,也就是Manacher's Algorithm。說真的,大部分解釋真的很難看懂他們到底在說些什麼。java

    你們都知道,求迴文串時須要判斷其奇偶性,也就是求aba 和abba 的算法略有差距。然而,這個算法作了一個簡單的處理,很巧妙地把奇數長度迴文串與偶數長度迴文串統一考慮,也就是在每一個相鄰的字符之間插入一個分隔符,串的首尾也要加,固然這個分隔符不能再原串中出現,通常能夠用‘#’或者‘$’等字符。例如:
原串:abaab
新串:#a#b#a#a#b#
這樣一來,原來的奇數長度迴文串仍是奇數長度,偶數長度的也變成以‘#’爲中心奇數迴文串了
接下來就是算法的中心思想,用一個輔助數組Radius[ ],記錄以每一個字符爲中心的最長迴文半徑,也就是R[i]記錄以Str[i]字符爲中心的最長迴文串半徑。R[i]最小爲1,此時迴文串爲Str[i]自己。
咱們能夠對上述例子寫出其R數組,以下
新串: # a # b # a # a # b #
R[] : 1 2 1 4 1 2 5 2 1 2 1
咱們能夠證實R[i]-1 就是以Str[i]爲中心的迴文串在原串當中的長度。
證實:
一、顯然L=2*R[i]-1 即爲新串中以Str[i]爲中心最長迴文串長度。算法

二、以Str[i]爲中心的迴文串必定是以#開頭和結尾的,例如「#b#b#」或「#b#a#b#」因此L 減去最前或者最後的‘#’字符就是原串中長度的二倍,即原串長度爲(L-1)/2,化簡的R[i]-1。依次從前日後求得R數組就能夠了,這裏用到了DP(動態規劃)的思想, 也就是求R[i] 的時候,前面的R[]值已經獲得了,咱們利用迴文串的特殊性質能夠進行一個大大的優化。數組

敲黑板,劃重點!下面是該算法的核心思想優化

如何去求這個R數組,是求解這道題的關鍵。咱們使用兩個輔助變量 id 和 mx ,其中 id 爲已知的最長的迴文子串的中心位置,那麼 mx 則爲 id + R[id],也就是這個子串的右邊界。ui

而後當你用變量 i 依次循環的時候,會有如下結論:spa

若是mx > i,那麼R[i] >= MIN(R[2 * id - i], mx - i)code

也就是說:字符串

//記j = 2 * id - i,也就是說 j 是 i 關於 id 的對稱點(j = id - (i - id))
if (mx - i > R[j]) 
    R[i] = R[j];
else /* R[j] >= mx - i */
    R[i] = mx - i; // R[i] >= mx - i,取最小值,以後再匹配更新。

一圖勝千言吧,看圖說話:博客

當 mx - i > R[j] 的時候,以S[j]爲中心的迴文子串包含在以S[id]爲中心的迴文子串中,因爲 i 和 j 對稱,以S[i]爲中心的迴文子串必然包含在以S[id]爲中心的迴文子串中,因此必有 R[i] = R[j],見下圖。it



當 R[j] >= mx - i 的時候,以S[j]爲中心的迴文子串不必定徹底包含於以S[id]爲中心的迴文子串中,可是基於對稱性可知,下圖中兩個綠框所包圍的部分是相同的,也就是說以S[i]爲中心的迴文子串,其向右至少會擴張到mx的位置,也就是說 R[i] >= mx - i。至於mx以後的部分是否對稱,就只能老老實實去匹配了。

 



對於 mx <= i 的狀況,沒法對 P[i]作更多的假設,只能P[i] = 1,而後再去匹配了.

基於以上思路,奉出JAVA代碼

public class LongestPalindrome {
	
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		while(scanner.hasNext()){
			String s = scanner.next();
			System.out.println(CalPalindrome(BuildStr(s)));
		}
	}
	
	public static char[] BuildStr(String s){
		StringBuilder sb = new StringBuilder(s);
		char[] ch = new char[2*s.length()+1];
		for (int i = 1; i <= ch.length; i++) {
			if(i%2==0) ch[i-1] = sb.charAt(i/2-1);
			else ch[i-1] = '#';
		}
		return ch;
	}
	
	public static int CalPalindrome(char[] ch){
		int mx = 0;
		int id = 0;
		int[] R = new int[ch.length];
		for (int i = 0; i < R.length; i++) {
			R[i] = 1;//由於數組中最小的值也是1,先初始化一下
		}
		for (int i = 1; i < ch.length; i++) {
			R[i] = mx>i?Math.min(R[2*id-i], mx-i):1;
			while(i-R[i]>=0 && i+R[i]<ch.length && ch[i+R[i]]==ch[i-R[i]]) R[i]++;
			if((i+R[i])>mx){
				mx = i+R[i];
				id = i;
			}
		}
		Arrays.sort(R);
		return R[R.length-1]-1;
	}
}
相關文章
相關標籤/搜索