求最长回文字串

声明

方法均是学习参考leetcode的算法,自己学习,都是自己做下总结,并保存记录。因为很多算法没给代码参考,所以我自己实现的代码在这保存一下。有什么不对的地方,各位大佬轻点喷。。。
下面是leetcode的链接:
leetcode(中文版)链接:最长回文子串

题目描述

leeticode 的描述:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:

输入: “cbbd”
输出: “bb”

方法1:利用最长公共子串

下面是leetcode原话
算法
有些人会忍不住提出一个快速的解决方案,不幸的是,这个解决方案有缺陷(但是可以很容易地纠正):
反转 SS,使之变成 S’S‘ 。找到 SS 和 S’S’ 之间最长的公共子串。然后判断他们的索引是否相同。
如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。
这给我们提供了一个复杂度为 O(n的平方) O(n 的平方)动态规划解法,它将占用 O(n^2)O(n 的平方 ) 的空间(可以改进为使用 O(n)O(n) 的空间)。
最长公共字串可以参考这篇文章:最长公共子串
因为这个算法Leetcode没给参考代码,
下面是我实现的代码:有什么错误的地方,各位大佬轻点喷

import java.util.HashMap;
import java.util.Map;
public class leetcode1 {
	//主函数
	public static void main(String[] args) {
		String aabb="abac";
		System.out.println(longestPalindrome(aabb));
	}
	//实现方法
	public static String longestPalindrome(String s) {
		char[] ss=s.toCharArray();
		String zchw="";
	    String sf="";
	    //得到s反
	    for(int i=s.length()-1;i>=0;i--){
	            sf+=ss[i];
	    }
	   //求s与s反的最长公共字串,并判断索引
	   int[][] aa = new int[s.length()][s.length()];
	   /*构造字符矩阵如:
	    *      a  b  a  c
	    *    c          1
	    *    a 1
	    *    b    2
	    *    a 1     3
	    *    斜线最长的为最长字串  aba
	    * **/
	   int  max=-1,zzb=0;//记录最大值和所在行纵坐标
	   int sfysy=0;//用来计算字符串反的原y轴索引。s.chat(j)是原字符串y轴索引
	   for(int i=0;i<s.length();i++) {
		   for(int j=0;j<s.length();j++) {
			if(sf.charAt(i)!=s.charAt(j)) aa[i][j]=0;//判断索引是否相同。
			else {
				if(i==0||j==0) aa[i][j]=1;
				else aa[i][j]=aa[i-1][j-1]+1;
				sfysy=s.length()-1-(i-aa[i][j]+1);
				if(sfysy==j) { 
				  if(aa[i][j]>=max) {
					max=aa[i][j];
					zzb=i;
			    }
			  }
			}
		  }
	   }
	   for(int i=zzb,j=0;j<max;i--,j++) {
		   zchw+=sf.charAt(i);
	   }
		return zchw;
	   }
}

算法2暴力法

额。。。。这个我就不写了。要看的自己去看。。。

算法3动态规划

这个方法是改进暴力法。
求最长回文字串
下面是代码:

class Solution {
   public static String longestPalindrome(String s) {
		if (s == null || s.length() < 1) return "";
		String answer="";
	    int max=1;//记录最长回文的字符数,因为空的判断完了,至少是1
		int hwt=0;//记录最长回文字符的开始索引
		//按照leetcode算法理解所有的1字母就是从索引从第一个开始了。。第0个左边没有了不用看
		//同理不用运行到最右边的一个
		for(int i=1;i<s.length()-1;i++) {
			for(int j=1;;j++) {
				if((i-j)<0||(i+j)>=s.length()) break;//达到边界直接跳出这个循环
				//如果左右相同记录一下继续判断下一个,否则跳出
				if(judgepij(s,i-j,i+j)) {
					if((1+j*2)>=max) {
						max=1+j*2;
						hwt=i-j;
					}
				}
				else break;
			}
		}
		//接下来是有两个一样字母的如:aa,bb这种的,为了防止直接有aab这种,所以老老实实从第0个开始。。。。
		//但是出现abb就不用慌,本来就向后比了一个
		for(int i=0;i<s.length()-1;i++) {
			//首先找连续的一样的,找到了再开始循环如果之前max小于2,这里要跟新一***意这个细节
			if(s.charAt(i)==s.charAt(i+1)) {
				if(max<2) {
					max=2;
					hwt=i;
				}
				for(int j=1;;j++) {
					if((i-j)<0||(i+1+j)>=s.length()) break;//达到边界直接跳出这个循环
					if(judgepij(s,i-j,i+1+j)) {
						if((2+j*2)>=max) {
							max=2+j*2;
							hwt=i-j;
						}
					}
					else break;
				}
			}
		}
		//已经找到了max和hwt,下面获得这个数组就行了
		for(int i=hwt,j=0;j<max;i++,j++) {
			answer+=s.charAt(i);
		}
		return  answer;
	   }
    public static boolean judgepij(String s,int i,int j) {
		if(s.charAt(i)==s.charAt(j)) return true;
		else return false;
	}
}

这个感觉还能优化,希望大佬能提提意见,我只是把leetcode里面算法实现一下。。要喷的大佬轻点。。

算法4中心扩展算法

求最长回文字串
官方源码:

public String longestPalindrome(String s) {
    if (s == null || s.length() < 1) return "";
    int start = 0, end = 0;
    for (int i = 0; i < s.length(); i++) {
        int len1 = expandAroundCenter(s, i, i);//我们不难发现这个方法是查找1字母回文的长度
        int len2 = expandAroundCenter(s, i, i + 1);//这个是查找2字母回文的长度
        int len = Math.max(len1, len2);//选这两个里面最大的
        //end-start就是当前回文长度,如果len大于当前就进行刷新
        if (len > end - start) {
            start = i - (len - 1) / 2;//刷新回文头索引,至于为什么是i-(len-1)/2,因为如果len是偶数如abba              
            //从bb往两边找,i的值是1
            //start = i - len/ 2+1=1-4/2+1=0;把这个换成start = i - (len-1)/ 2=1-3/2=0也对,就是把那个1再len里面减了
            //如果是奇数如aba,此时i=1, start = i - (len - 1) / 2=1-2/2=0;
            end = i + len / 2;//刷新回文尾索引,无论奇偶原理同上
        }
    }
    return s.substring(start, end + 1);
}
//中心扩展的实现
private int expandAroundCenter(String s, int left, int right) {
    int L = left, R = right;
    //如果两边两边相等并且不会越界就继续往两边扩展
    while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
        L--;
        R++;
    }
    return R - L - 1;//得到长度
}

官方代码很短,这个和上面的动态规划其实思想差不多的,就是从中心开始,初始化是1的就以单字母往两边找,二字母回文就直接从这两个字母往两边找。如abba,这里2字母回文从bb往两边开始查找。
区别就是他一次遍历直接把一字母的和二字母的找完了
并且代码简短了许多,减少了空间浪费。将空间复杂度o(n的平方)减少到了o(1)。
各位大佬有比官方更优解吗,希望分享一下。