LeetCode 原题,这里主要说一下自己的动态规划解法和思路,希望对大家理解这个有所帮助。
暴力解法为遍历所有子串,逐个判断是否是回文字串。
接下来我们来优化暴力解法,暴力解法的问题在于没有用到回文字串的特性,只是用了定义去检验一个字串是不是回文,所以这个题的题眼在于利用回文字串的特性。
如果一个字串是回文字串,那么去掉左右两边的字符之后依然是回文。
也可以说是一个回文字串,左右两边加上相同的字符,也是回文字串。
使用索引 i 和 j 来表示一个字符串从索引 i 到 j 的子串,则:
dp[i][j]表示索引i到j的子串是否是回文
dp[i][j] = true表示是回文,反之则为false
dp[i][i]只有一个字符,必是回文
关键点在于找到关系:dp[i][j] = dp[i + 1][j - 1] && s.charAt(i) == s.charAt(j);
长的子串dp[i][j]依赖于短的子串dp[i + 1][j - 1],所以由短到长依次计算
1、先计算一个字符,全为true
2、再计算两个字符,如果两个字符一样则为true
3、然后计算大于三个字符,直到整个字符串
1、定义二维数组存储dp的结果值
2、单个字符(起点终点索引相同)全部为true
3、两个字符如果字符相同为true(注意数组不要越界)
4、依次循环三个字符、四个字符......
5、有起点索引 i,有子串长度 k 则可以得到终点索引 j (同样注意数组越界问题)
6、比较回文子串长度与保存的result长度
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
boolean[][] dp = new boolean[len][len];//<1>
String result = s.substring(0, 1);
for (int i = 0; i < len; i++) {
dp[i][i] = true;//<2>
}
for (int i = 0; i < len - 1; i++) {
dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1);//<3>
if (dp[i][i + 1]) {
result = s.substring(i, i + 1 + 1);
}
}
//<4>
for (int k = 3; k <= len; k++) {
for (int i = 0; (i + k) <= len; i++) {
int j = i + k - 1;//<5>
dp[i][j] = dp[i + 1][j - 1] && s.charAt(i) == s.charAt(j);
if (dp[i][j] && (j - i + 1) > result.length()) {//<6>
result = s.substring(i, j + 1);
}
}
}
return result;
}
}