问题描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
解题思路
对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串{"ababa’’},如果我们已经知道{“bab”} 是回文串,那么 {"ababa’’} 一定是回文串,这是因为它的首尾两个字母都是“a”。
根据这样的思路,我们就可以用动态规划的方法解决本题。
如果一个字符串的头尾两个字符都不相等,那么这个字符串一定不是回文串;
如果一个字符串的头尾两个字符相等,才有必要继续判断下去。
- 如果里面的子串是回文,整体就是回文串;
- 如果里面的子串不是回文串,整体就不是回文串。
即:在头尾字符相等的情况下,里面子串的回文性质据定了整个子串的回文性质,这就是状态转移。因此可以把「状态」定义为原字符串的一个子串是否为回文子串。
第 1 步:定义状态
dp[i][j] 表示子串 s[i…j] 是否为回文子串,这里子串 s[i…j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。
第 2 步:思考状态转移方程
在这一步分类讨论(根据头尾字符是否相等),根据上面的分析得到:
dp[i][i] = (s[i]==s[j]) abd dp[i][j] 当j-i<=1时
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1] 当j-i>1时
实现代码
class Solution {
public String longestPalindrome(String s) {
int n=s.length();
char [] arrStr=s.toCharArray(); //将字符串转化为数组,便于运算
int maxi=0,maxj=0; //标记最长回文子串的下标
int maxlen=0;
//标记数组,标记从起点i到终点j之间的字符是否是回文子串,默认全为false
boolean [][] f=new boolean[n][n];
//这里i是终点,j是i之前的字符,即以i为终点,遍历i之前的字符是否为回文子串
//注意这里的顺序,一定要保证后面的回文子串利用到前面的回文子串时,前面的回文子串一定被计算出来过了。
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
//当i-j<=1时,判读那两个字符是否相等,相等即为回文子串,这里包含了i与j重合的情况
if(i-j<=1){
if(arrStr[i]!=arrStr[j]){
f[j][i]=false;
}else{
f[j][i]=true; //相等时如果大于当前最长回文子串的时候,就记录下来
if(maxlen<(i-j+1)){
maxlen=i-j+1;
maxi=i;
maxj=j;
}
}
}else{
//当i-j>1时,需要判断当前两个字符是否相等,并且还要判断子字符串是否为回文子串。
if(arrStr[i]==arrStr[j] && f[j+1][i-1]==true){
f[j][i]=true;
if(maxlen<(i-j+1)){
maxlen=i-j+1;
maxi=i;
maxj=j;
}
}else{
f[j][i]=false;
}
}
}
}
String res="";
for(int i=maxj;i<=maxi;i++){
res+=arrStr[i];
}
return res;
}
}