LeetCode第5题-最长回文子串-java实现-图解思路与手撕代码
一、题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
例如:s=“babad”,最长回文子串是"aba"
s=“bbs”,最长回文子串是"bb"
二、解题思路与代码实现
1.解题思路
最开始设想暴力匹配法和中心扩散法,但都没有动态规划法简洁明了。
动态规划旨在充分利用先前查找结果,避免对单一字符多次查找。
首先定义一个数组dp[i][j]表示字符串从i到j是否构成回文,初始化dp数组斜线为true,表示单一字符也可以是长度为1的回文,并且这个初始化的步骤是后续更新dp数组的基础。
我们只需要对右上半部分矩阵进行计算,更新公式如下
dp[i][j]=dp[i+1][j-1]+s[i]==s[j]
这个公式的含义是,对于区间范围是(i,j)的字符串,判断其是否是回文的判断标准是,第i个字符和第j个字符是否相等以及区间范围(i+1,j-1)的字符串是否是回文。
在更新dp数组的过程中记录下最大长度以及起始下标,便于最后返回字符串。
2.相加部分实现
代码如下(示例):
首先判断字符串长度是否是0和1,如果是就不需要判断,直接返回。
定义数组dp,最大长度maxLen以及起始位置maxStart。
由于更新公式中对(i,j)的判断需要使用到(i+1,j-1),所以我们的更新方向是先更新左下角的,再更新右上角的,即先更新列,再更新行。
在两个for循环中需要判断i和j的关系,如果相等或者相差为1,就表示此时的字符区间是一个字符或者两个字符,那直接对dp数组赋值即可,如果i和j相差大于等于2,就需要使用上述更新公式,对dp数组进行更新。
更新过程中不断比较更新maxLen的值,并记录下maxStart。
最后返回字符串。
private static String longestPalindromeWithDP(String s) {
if (s.length() < 2) {
return s;
}
char[] ch = s.toCharArray();
boolean[][] dp = new boolean[s.length()][s.length()];
for (int i = 0; i < dp.length; i++) {
dp[i][i] = true;
}
int maxLen = 0;
int maxStart = 0;
for (int j = 0; j < dp.length; j++) {
for (int i = 0; i < j; i++) {
if (i == j) {
dp[i][j] = true;
if (maxLen < 1) {
maxLen = j - i + 1;
maxStart = i;
}
} else if (j - i == 1) {
if (ch[i] == ch[j]) {
dp[i][j] = true;
if (maxLen < j - i + 1) {
maxLen = j - i + 1;
maxStart = i;
}
}
} else {
if (ch[i] == ch[j] & dp[i + 1][j - 1]) {
dp[i][j] = true;
if (maxLen < j - i + 1) {
maxLen = j - i + 1;
maxStart = i;
}
}
}
}
}
return s.substring(maxStart, maxStart + maxLen);
}
总结
这道题的难度在于理解回文的特点,以及熟练使用动态规划。
在动态规划中如何进行初始化,如何确定更新公式,以及更新方向等一些小细节,作为一道动态规划的练习题非常有价值。