Leetcode之最长回文子串

问题描述

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

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

解法

思路很简单,首先想到若情况为回文字符串奇数个字符时,中间的字符为轴。比较两边的字符是否相同。当回文串字符为偶数时,刚开始假想它以空气为轴,则找不到其他字符的下标。于是想到在每个字符包括两边都插入一个特殊字符比如(),这样构成的回文字符串一定是奇数长度。也就是一定存在中间的某个字符,使得两边以它(可能是特殊字符也可能原字符串中的字符)为轴,并且此字符串的两边都以结尾。

public static void longestPalindrome(String s) {
		//在每两个字符中间插入特殊字符,保证回文字符串一定是奇数长度
		String str=s.replace("", "*");
		int maxl=1;
		int index=0;
		//从左到右遍历,寻找以每一个字符为轴的最大长度
		for(int i=1;i<str.length();i++) {
			if(maxl<=2*Math.min(i,str.length()-i-1)+1) {//两边的长度可以超过最大长度
				for(int j=1;;j++) {
					if(j<=i&&j<=str.length()-i-1) {//两边的长度可以取到的值
						if(str.charAt(i-j)!=str.charAt(i+j)) {//这一个字符不是回文串中的
							int l=2*j-1;
							if(maxl<l) {
								maxl=l;
								index=i;
							}
							break;
						}
					}else {//到头了都是回文串
						int l=2*j-1;
						if(maxl<l) {
							maxl=l;
							index=i;
						}
						break;
					}
				}
			}
		}
		//得到字符串
		StringBuilder sb=new StringBuilder();
		for(int i=index+1-(maxl-1)/2;i<=index-1+(maxl-1)/2;i++) {
			if(i%2!=0) {
				sb.append(str.charAt(i));
			}
		}
    }

结果

官方解法

方法一 动态规划

使用P[i][j]表示Si到Sj是否是一个回文串。
动态规划转移方程可以写为P[i][j]=P[i+1][j-1]&Si,即先看中间再加上首尾两端。
边界条件:因为单个的字符为回文串,P[i][i]=true,两个相邻的相同字符为回文串if Si==Si+1则P[i][i+1]=true
根据这个思路我自己写了一个代码。跑出来的效果很差。

public static String longestPalindrome(String s) {
		if(s==null||s.isEmpty())
			return "";
		boolean[][]P=new boolean[s.length()][s.length()];
		//边界
		int length=1;
		int start=0,end=0;
		for(int i=0;i<s.length()-1;i++) {
			if(s.charAt(i)==s.charAt(i+1)) {
				P[i][i+1]=true;
				if(length<2) {
					length=2;
					start=i;
					end=i+1;
				}
			}
			P[i][i]=true;
		}
		P[s.length()-1][s.length()-1]=true;
		//
		for(int i=0;i<s.length()-2;i++) {
			for(int j=0;j<s.length()-i-2;j++) {
				if(P[j+1][j+i+1]&&s.charAt(j)==s.charAt(j+i+2)) {
					P[j][i+j+2]=true;
					int l=i+3;
					if(length<l) {
						length=l;
						start=j;
						end=i+j+2;
					}
				}
			}
		}
		s=s.substring(start, end+1);
		return s;
    }

方法二 中心扩展

方法二的本质即为:我们枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。这个算法思想与我的很相似。但是跑出来的效果比我好。

class Solution {
    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);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                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;
    }
}

方法三 Manacher算法

posted @ 2020-09-10 17:51  小帆敲代码  阅读(196)  评论(0编辑  收藏  举报