(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();




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值