题目
给你一个字符串 s
,找到 s
中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/lo…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
结题思路
动态规划
每次计算充分利用之前的计算结果,用空间换时间
核心思想:一个回文串去掉两头以后,剩下的部分依然是回文串
-
状态
dp[i][j]
表示子串s[i...j]
是否为回文串 -
状态转换方程
dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]
-
边界条件 除去两边的两个元素后,剩下的元素个数如果小于2(小于等于1),就认为是回文串,不用进行验证。
即:aba
先判断两边是都相等,如果相等就看去除两边后中间元素个数小于2,则是回文数。用下标表示为:
j-1 - (i+1) + 1 < 2
,换算结果为:j - i < 3
-
初始化 对角线元素只有一个字母,是回文串。
dp[i][i] = true
-
输出 得到一个状态为true的时候,记录起始位置和长度,填表完成后进行截取。
这里为什么还需要记录长度?不是在dp表里边记录了么?
实际上,dp表只是记录s[i...j]
是不是回文串,他能提供的信息只是是或者不是,因此并不能知道哪个是最长的,以及从哪里开始,因此要记录这写信息,方便之后截取字符串。
算法流程
要时刻明确:动态规划就是维系一张二维表,然后当前结果根据之前计算的结果计算而来,不用重新算
代码
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j]表示s[i...j]是否是回文串
boolean[][] dp = new boolean[len][len];
// 对角线初始化为true
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (charArray[i] != charArray[j]) {
// 先判断两边是否相等
dp[i][j] = false;
} else {
// 当子串长度为2或者3的时候不需要进行验证
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i+1][j-1];
}
}
// 只要dp[i][j] == true成立,就记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
测试用例
public class Solution_Test_5 {
Solution_5 solution_5 = new Solution_5();
@Test
public void test1() {
String str = "babad";
String ans = solution_5.longestPalindrome(str);
assertEquals(ans, "bab");
}
@Test
public void test2() {
String str = "cbbd";
String ans = solution_5.longestPalindrome(str);
assertEquals(ans, "bb");
}
@Test
public void test3() {
String str = "a";
String ans = solution_5.longestPalindrome(str);
assertEquals(ans, "a");
}
@Test
public void test4() {
String str = "ac";
String ans = solution_5.longestPalindrome(str);
assertEquals(ans, "a");
}
}