给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母组成
关键词:动态规划
public String longestPalindrome(String s) {
//定义起始位置和回文子串长度,始终记录最长的回文子串
int start = 0,maxLen = 1;
int len = s.length();
if(len<2) return s;
//用来存回文状态的二维数组
boolean[][] dp = new boolean[len][len];
for(int i=0;i<len;i++){
dp[i][i] = true; //将单个字符的字符串初始化为回文子串true
}
char[] chars = s.toCharArray();
//从子串长度开始遍历,减少遍历次数
for(int L=2;L<=len;L++){
for(int i=0;i<len;i++){
int j = i+L-1; //根据L=j-i+1得到j的位置
//如果j大于数组的最大边界,则跳出当前的循环
if(j>=len)
break;
//如果两端的字符不等,则说明不是回文子串
if(chars[i]!=chars[j]){
dp[i][j] = false;
}
else {
//如果子串两端的字符相等,且长度小于3,则实锤回文子串
if(j-i<3){
dp[i][j] = true;
}else {
//转换为求i+1到j-1位置的子串是否为回文子串
dp[i][j] = dp[i+1][j-1];
}
}
//如果判断i到j的状态为回文子串,且长度比最大长度还大
//则更新起始位置start和最大长度,继续遍历
if(dp[i][j]==true && j-i+1>maxLen){
maxLen = j-i+1;
start = i;
}
}
}
return s.substring(start,start+maxLen);
}
解题思路:
动态规划:
输入字符串为str,转换为字符数组为char[]
i到j的字串用S[i][j]表示
= S[i][j]为回文子串 -> S[i+1][j-1]为回文子串 && char[i]==char[j]
= 临界情况str长度为1和2,如果为1,该字符串必定为回文;
如果为2,判断char[i]==char[i+1],真则为回文,反之。
= 用二维数组dp[i]j]表示字符串从i到j的回文情况,定义start记录回文子串起始位置,maxLen记录回文子串长度,每当出现更长的回文子串则更新起始位置start和最大长度maxLen
= 先将二维数组初始化,将str单个字符的回文状态都置为true
for(int i=0;i<str.length;i++){
dp[i][i] = true;
}
两种遍历思路:
1)遍历子串的长度,从2到str.length
= 从回文子串长度L为2开始遍历,终点为str.length;
内部循环从i=0开始遍历,可以确认j的位置为i + L -1,判断char[i]和char[j]的值
临界点:if j>str.length,说明已超过数组上限,直接退出当前长度L为x的循环
if char[i]!=char[j],则dp[i][j]为false,不为回文子串;
if char[i] == char[j],则进一步进行判断,
如果长度j-i<3,可能为1和2,此时一定为回文,dp[i][j] = true
如果是平常情况,转移判断:dp[i][j] = dp[i+1][j-1]
if dp[i][j]==true,则说明字符串i到j是回文,直接更新起始位置start和最大长度maxLen
start = i, maxLen = j - i + 1
int len = str.length();
for(int L=2;L<=len;L++){
for(int i=0;i<len;i++){
j = i+L-1;
if(j > len) break;
if(char[i]!=char[j]) dp[i][j] = false;
else{
if(j-i < 3) dp[i][j] = true;
else dp[i][j] = dp[i+1][j-1];
}
if(dp[i][j]==true && j-i+1>maxLen){
start = i;
maxLen = j-i+1;
}
}
}
2)遍历i和j,先遍历j,然后遍历i,保证左下角先填满,避免全部遍历一遍
其余判断逻辑和1)中的一样。
for(int j=1;j<len;j++){
for(int i=0;i<j;i++){
if(char[i]!=char[j]) dp[i][j] = false;
else{
if(j-i < 3) dp[i][j] = true;
else dp[i][j] = dp[i+1][j-1];
}
if(dp[i][j]==true && j-i+1>maxLen){
start = i;
maxLen = j-i+1;
}
}
}
总结:
1.对于问题的处理需要借助前一阶段的结果来判断这种场景,请考虑动态规划:确认好问题的转换逻辑,找到最小问题和临界条件;
2.在定义状态数组时,善于结合题目,本题中很好的用了二维数组来记录两个字符之间的字符串是否为回文子串的状态,同时巧妙用for循环对单个字符组成的字符串初始化为true,设置为回文子串