1.题目描述
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
2.解题思路
2.1(方法一)动态规划
首先介绍第一种解题的思路“动态规划”,“动态规划”达到时间复杂度为 O ( n 2 ) O(n^2) O(n2),但是空间复杂度会也是 O ( n 2 ) O(n^2) O(n2),比下面的那种方法大许多,详情见下面“中心扩展法”。首先定义“布尔数组” d p [ i ] [ j ] dp[i][j] dp[i][j]表示以数组 M [ i ] M[i] M[i]开始,以 M [ j ] M[j] M[j]结束的字符串是否是“回文串”,此时可以根据 M [ i ] M[i] M[i]是否等于 M [ j ] M[j] M[j]分为两种情况:
- 当 d p [ i + 1 ] [ j − 1 ] = = t r u e dp[i+1][j-1] == true dp[i+1][j−1]==true时,若 M [ i ] = = M [ j ] M[i] == M[j] M[i]==M[j],则 d p [ i ] [ j ] = t r u e dp[i][j] = true dp[i][j]=true
- 当 d p [ i + 1 ] [ j − 1 ] = = f a l s e dp[i+1][j-1] == false dp[i+1][j−1]==false时,则 d p [ i ] [ j ] = f a l s e dp[i][j] = false dp[i][j]=false
可以写出如下状态转移方程:
d
p
[
i
]
[
j
]
=
d
p
[
i
+
1
]
[
j
−
1
]
&
&
M
[
i
]
=
=
M
[
j
]
dp[i][j] = dp[i+1][j-1] \&\& M[i] == M[j]
dp[i][j]=dp[i+1][j−1]&&M[i]==M[j]
同时需要注意边界情况就是,回文串为一个字符或者两个字符的情况,他们的判断情况如下
回文为“一字符”:
d
p
[
i
]
[
i
]
=
t
r
u
e
dp[i][i] = true
dp[i][i]=true
回文为“二字符”:
d
p
[
i
]
[
i
+
1
]
=
M
[
i
]
=
=
M
[
i
+
1
]
dp[i][i+1] = M[i]==M[i+1]
dp[i][i+1]=M[i]==M[i+1]
算法的第一步:
先处理“一字符”和“二字符”字符串的回文情况:如左图:
算法的第二步:
循环遍历填写 d p [ i ] [ j ] dp[i][j] dp[i][j],过程中记录的最长回文子串则是所求,如右图:
Code
class Solution {
public String longestPalindrome(String s) {
if(s.equals(""))return "";
char[] M = s.toCharArray();
boolean[][] dp = new boolean[M.length][M.length];
int max = 1; //记录最长回文子串的长度
int left = 0;
int right = 0;
//初始化一字母和二字母的回文
for(int i = 0; i < M.length - 1; i++){
dp[i][i] = true;
if(M[i] == M[i+1]){
dp[i][i+1] = true;
left = i;
right = i + 1;
max = 2;
}
}
dp[M.length - 1][M.length - 1] = true;
for(int j = 2; j < M.length; j++){
for(int i = 0; i < j; i++){
if(dp[i + 1][j - 1] && M[i] == M[j]){
dp[i][j] = true;
if(j-i+1 > max){left = i; right = j; max = j-i+1;}
}
}
}
return s.substring(left, right + 1);
}
}
2.2(方法二)中心扩展算法
另外的一个思路则根据算法的运算过程,可以成为“中心扩展法”,顾名思义,则是遍历字符串中的每一个字符,以该字符为回文串中心,分别左右向外扩展,当遇到两个字符相等时,则回文串增长2,同时继续向外扩展;
这里需要注意的是:回文串的中心可能有两种情况,一种是一字符,一种是双字符,所以每次遍历字符向外扩展时,需要分为中心为i和中心为i+1两种情况,如下代码
Code
class Solution {
public String longestPalindrome(String s) {
if(s.length()==0){
return s;
}
int start = 0 ;
int end = 0 ;
for(int i = 0 ; i < s.length() ; i++) {
int len1 = expand(s,i,i+1); //回文中心为双字符
int len2 = expand(s,i,i); //回文中心为单字符
int max = Math.max(len1,len2);
if(max > end - start) { //若找到更长的回文串,则更新回文串的起始位置和结束位置
start = i-(max-1)/2;
end = i+(max/2);
}
}
return s.substring(start,end+1);
}
public int expand(String s , int start , int end) {
while(start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
start--;
end++;
}
return end-start-1;
}
}