最长回文子串

在leetcode中做到最长回文子串,看了别人的解法,我自己也总结了一下(java实现)

leetcode链接

暴力解决 时间复杂度O(n3)

思路:双层循环,每次判断【i,j】范围是否时回文,如果是回文,则比较保存的长度和当前长度。若当前的子串长,则更新起始坐标和长度。每次保存起始坐标和长度,最后做字符串的截取。

public static String longestPalindrome(String s) {
	if(s.length()<1)
		return "";
	int pos=0,len=1;
	for(int i=0;i<s.length();i++) {
		for(int j=i+1;j<s.length();j++) {
			if(Judge(s, i, j)&&len<j-i+1) {
				pos=i;
				len=j-i+1;
			}
		}
	}
	return s.substring(pos,pos+len);
}
public static boolean Judge(String s,int from,int to) {
	while(from<=to) {
		if(s.charAt(from)==s.charAt(to)) {
			from++;
			to--;
		}
		else {
			return false;
		}
	}
	return true;
}

动态规划 时间复杂度O(n2)

思路:在暴力解决的基础上做数据的存放,避免反复计算。bp[i][j]表示【i,j】是否为回文。

public static String longestPalindrome(String s) {
	 int len=s.length();
	 if(len==0)
		 return "";
	 boolean bp[][]=new boolean[len][len];
	 for(int i=0;i<len;i++) {
		 bp[i][i]=true;
	 }
	 int pos=0,length=1;
	 for(int i=0;i<len;i++) {
		 for(int j=0;j<i;j++) {
			if(s.charAt(i)!=s.charAt(j)) {
				bp[j][i]=false;
				continue;
			}
			if(i-j<3) {
				bp[j][i]=true;
			}
			else {
				bp[j][i]=bp[j+1][i-1];
			}
			if(bp[j][i]&&i-j+1>length) {
				pos=j;
				length=i-j+1;
			}
		 }
	 }
	 return s.substring(pos,pos+length);
 }

中心扩散 时间复杂度O(n2)

思路:回文存在奇偶两种情况。所以在遍历的时候判断以当前i,或者i到i+1为中心向外扩散的最大长度。如若比原先保存的要大,则进行更新,最后返回截取的字符串

public static String longestPalindrome(String s) {
	if(s.length()<2)
		return s;
//		考虑到奇偶两种情况
	int pos=0,len=1;
	int one,two,max;
	for(int i=0;i<s.length()-1;i++) {
		one=expendAroundCenter(s, i, i);
		two=expendAroundCenter(s, i, i+1);
		max=Math.max(one, two);
		if(max>len) {
			pos=i-(max-1)/2;
			len=max;
		}
	}
	return s.substring(pos,pos+len);
}
	
//	 获得以[from,to]扩展开的最大回文串
public static int expendAroundCenter(String s,int from,int to) {
	while((from>=0&&to<s.length())&&s.charAt(from)==s.charAt(to)) {
		from--;
		to++;
	}
	return to-from-1;
}

manacher 算法 时间复杂度O(n)

思路:

  • 首先,对字符串进行填充,每个字符间以及首尾都填充指定字符,使得填充后的字符的长度为奇数,这样就不用对回文进行奇偶判断了
  • maxright为已存储的回文子串的最有边距,center为该状态下的回文中心
    i 为当前遍历下标,mirror为i相对于center的镜像,数组f用来存储对应以i为中心的回文子串半径。
  • 若maxright小于i 则直接依据中心扩散找到最长回文子串
  • 若maxight大于i 此时center必定小于i,这时f[i]的值先取f[mirror]和maxright-i中的小的那个,在以f[i]为半径继续进行中心扩散,用以更新f[i]。理由如下:
    第一种情况:maxright-i<f[mirror] 此时 f[i] 必定等于maxright-i
    第二种情况:maxright-i=f[mirror] 此时 f[i]大于等于f[mirror] ,这种情况下需要以f[mirror]继续进行中心扩散
    第三种情况:maxright-i>f[mirror] 此时 f[i]等于f[mirror]
    想不通的自己画图想想。注意细节(f[mirror]为已知,即半径确定为f[mirror],超出半径不符合回文要求。此外maxright、center、maxleft(假设用,用于分析)这三个是由镜像关系的。)
    最后合并情况就是先去maxright-i和f[mirror]中的小的那个,然后在以此为半径向外中心扩散尝试更新(尽可能使半径变大)。
public static String longestPalindrome(String s) {
	if(s.length()<2)
		return s;
	int mirror=0,i,maxright=0,center=0;
	int pos=0,length=1;//最大回文子串信息存储
	int len=s.length();
	int slen=2*len+1;
	int f[]=new int[slen];  //存储结果
	String adddivider=addDividers(s, '#');//插入分隔符后的字符串
	int left,right;
	for(i=0;i<slen;i++) {
		// 从左到右依次处理的,一旦maxright大于遍历i,此时center 必定比i 要小
		if(maxright>i) {
			mirror=2*center-i;
			f[i]=Math.min(f[mirror], maxright-i);
		}
		
		// 根据 镜像计算出来的的 小回文子串向两端继续扩散
//			此时回文扩散中心为i    当前满足的回文半径为f[i]
		left=i-f[i]-1;
		right=i+f[i]+1;
		while((left>=0&&right<slen)&&adddivider.charAt(left)==adddivider.charAt(right)) {
			f[i]++;//半径加一
			left--;
			right++;
		}
		
		
		//  更新maxright和center的值
		if(i+f[i]>maxright) {
			maxright=i+f[i];
			center=i;
		}
		
		//  更新最大回文子串
		if(f[i]>length) {
			pos=(i-f[i])/2;
			length=f[i];
		}
		
	}
	return s.substring(pos,pos+length);
}
	//  插入分隔符,使字符串  s的长度恒为奇数
public static String addDividers(String s,char divider) {
	char []charArray=s.toCharArray();
	int len=charArray.length;
	StringBuilder build=new StringBuilder();
	for(int i=0;i<len;i++) {
		build.append(divider);
		build.append(charArray[i]);
	}
	build.append(divider);
	return build.toString();
}

测试入口

public static void main(String[] args) {
	// TODO Auto-generated method stub
	String s="cbbd";
//		String s="aaaa";
//		String s="abcda";
//		String s="babad"; 
	System.out.println(longestPalindrome(s));

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值