问题描述
给定一个字符串 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算法
略