1. 程式人生 > >(java)求最長迴文子串

(java)求最長迴文子串

一開始看到這個題目的時候,我就想到了第一種思路:

(1)從頭遍歷每個字元,從該字元向兩邊擴充套件,直到字串最長界限或者兩邊擴充套件的字元不相等為止,記錄每個字元搜尋的長度,並且找最大的即為最長迴文子串個數。演算法優化的地方(當一個字元的擴充套件長度為整個字串長度時候,停止搜尋,列印就行,當然這只是一個小小的改進,因為我看到有那種500個字元都是一樣的資料)

AC程式碼如下:

 public String longestPalindrome(String s) {
         StringBuilder r=new StringBuilder();
			int maxcount=1;
			int id=0;
			 StringBuilder ss=new StringBuilder();
			 ss.append('#');
			for(int i=0;i<s.length();i++){
				ss.append(s.charAt(i));
				ss.append('#');
			}
			for(int i=1;i<ss.length()-1;i++){
				  int tempcount=1;
			      int j=i-1;
			      int k=i+1;
			      while(j>=0 && k<ss.length() && ss.charAt(j)==ss.charAt(k)){
			    	  tempcount++;
			    	  j--;
			    	  k++;
			      }
			      if(tempcount>maxcount){
			    	  maxcount=tempcount;
			    	  id=i;
			    	  if(id-maxcount+1==0 && id+maxcount==ss.length()){
			    	      break;
			    	  }
			      }
			}
			String tempr=ss.substring(id-maxcount+1,id+maxcount);
			for(int i=1;i<tempr.length();i=i+2){
				r.append(tempr.charAt(i));
			}
			return r.toString();
    }
   (2)後來看到一個演算法Manacher演算法,據說是O(N)的,看了看人家的部落格,大致寫的最容易理解的是下面:

(當然為了統一奇偶數,我們考慮了中間插入一個字元,這個上面也是用到了),預設下面就是用到了插入字元的情況。

我們需要一個輔助陣列rad[],用rad[i]表示第i個字元的迴文半徑,rad[i]的最小值為1,即只有一個字元的情況,現在問題轉變成如何求出rad陣列。
假設現在求出了rad[1, ..., i],現在要求後面的rad值,再假設現在有個指標k,從1迴圈到rad[i],試圖通過某些手段來求出[i + 1, i + rad[i] - 1]的rad值,其分析如下:
如圖1所示,黑色的部分是一個迴文子串,兩段紅色的區間對稱相等。因為之前已經求出了rad[i - k],所以可以避免一些重複的查詢和判斷,有3種情況:

                              尋找字串中最長迴文——Manacher演算法及其Java實現(POJ <wbr>3974)

①  rad[i] - k < rad[i - k]
如上圖,rad[i - k]的範圍為青色。因為黑色的部分是迴文的,且青色的部分超過了黑色的部分,所以rad[i + k]肯定至少為rad[i]-k,即橙色的部分。那橙色以外的部分就不是了嗎?這是肯定的,因為如果橙色以外的部分也是迴文的,那麼根據青色和紅色部分的關係,可以證明黑色部分再往外延伸一點也是一個迴文子串,這肯定是不可能的,因此rad[i + k] = rad[i] - k。

尋找字串中最長迴文——Manacher演算法及其Java實現(POJ <wbr>3974)

②  rad[i] - k > rad[i - k]
如上圖,rad[i-k]的範圍為青色,因為黑色的部分是迴文的,且青色的部分在黑色的部分裡面,根據定義,很容易得出:rad[i + k] = rad[i - k]。根據上面兩種情況,可以得出結論:當rad[i] - k != rad[i - k]的時候,rad[i + k] = min(rad[i] - k, rad[i - k])。

                             尋找字串中最長迴文——Manacher演算法及其Java實現(POJ <wbr>3974)

③  rad[i] - k = rad[i - k]
如圖,通過和第一種情況對比之後會發現,因為青色的部分沒有超出黑色的部分,所以即使橙色的部分全等,也無法像第一種情況一樣引出矛盾,因此橙色的部分是有可能全等的。但是,根據已知的資訊,我們不知道橙色的部分是多長,因此就需要再去嘗試和判斷了。

程式碼如下:

<span style="font-size:18px;">StringBuilder news = new StringBuilder();
		  news.append('#');
		for(int i=0;i<s.length();i++){
			news.append(s.charAt(i));
			news.append('#');
		}
		int[] rad=new int[news.length()];
		rad[0]=1;
		int maxrad=rad[0];
		int id=0;
		for(int i=1;i<news.length();i++){
			int temprad=1;
			if(i<maxrad+id){
			    if(rad[2*id-i]!=maxrad-i+id){
					rad[i]=Math.min(rad[2*id-i], maxrad-i+id);
					temprad=rad[i];
			    }else{
			         while (i - temprad >= 0 && i + temprad < news.length() && news.charAt(i - temprad) == news.charAt(i + temprad)) {
				        temprad++;
                    }
			    }
			}else{
			        while (i - temprad >= 0 && i + temprad < news.length() && news.charAt(i - temprad) == news.charAt(i + temprad)) {
				        temprad++;
                    }
			}
			rad[i]=temprad;
			if(temprad>maxrad){
				maxrad=temprad;
				id=i;
			}
		}
		System.out.println(maxrad-1);
		String tempr=news.substring(id-maxrad+1,id+maxrad);
		System.out.println(tempr);
		 StringBuilder r = new StringBuilder();
		for(int i=1;i<tempr.length();i=i+2){
			r.append(tempr.charAt(i));
		}
		return r.toString();</span>


再簡潔的程式碼如下:

 StringBuilder r=new StringBuilder();
			int maxcount=1;
			int id=0;
			 StringBuilder ss=new StringBuilder();
			 ss.append('#');
			for(int i=0;i<s.length();i++){
				ss.append(s.charAt(i));
				ss.append('#');
			}
			for(int i=1;i<ss.length()-1;i++){
				  int tempcount=1;
			      int j=i-1;
			      int k=i+1;
			      while(j>=0 && k<ss.length() && ss.charAt(j)==ss.charAt(k)){
			    	  tempcount++;
			    	  j--;
			    	  k++;
			      }
			      if(tempcount>maxcount){
			    	  maxcount=tempcount;
			    	  id=i;
			    	  if(id-maxcount+1==0 && id+maxcount==ss.length()){
			    	      break;
			    	  }
			      }
			}
			String tempr=ss.substring(id-maxcount+1,id+maxcount);
			for(int i=1;i<tempr.length();i=i+2){
				r.append(tempr.charAt(i));
			}
			return r.toString();