1. 题目
读题
https://leetcode.cn/problems/longest-palindromic-substring/description/
考查点
- 这道题的考查点是动态规划的基本思想和技巧,以及字符串的操作和判断。
2. 解法
思路
- 动态规划的思路是,用一个二维数组 dp[i][j] 表示字符串从 i 到 j 是否是回文串,然后遍历所有可能的子串,更新 dp 数组和最长回文子串的长度和起始位置。
- 动态规划的状态转移方程是:
- 如果 len == 1,那么 dp[i][j] = true。
- 如果 len == 2,那么 dp[i][j] = s.charAt(i) == s.charAt(j)。
- 如果 len > 2,那么 dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]。
- 动态规划的遍历顺序是,先遍历子串长度 len,从 1 到 n,然后遍历子串起始位置 i,从 0 到 n - len。
- 这道题的考查点是动态规划的基本思想和技巧,以及字符串的操作和判断。
- 动态规划是一种将复杂问题分解为子问题,然后利用子问题的解来求解原问题的方法。动态规划通常需要找到一个合适的状态表示和状态转移方程,以及一个合理的遍历顺序。
- 字符串的操作和判断是指如何获取字符串的长度,如何访问和比较字符串的字符,如何截取字符串的子串等。这些都是编程中常用的技能,需要熟练掌握。
代码逻辑
这个代码的思路是,用一个二维数组 dp[i][j] 表示字符串从 i 到 j 是否是回文串,然后遍历所有可能的子串,更新 dp 数组和最长回文子串的长度和起始位置。具体的步骤如下:
- 初始化一个 n x n 的布尔型数组 dp,其中 n 是字符串的长度。dp[i][j] 表示 s[i…j] 是否是回文串。
- 用一个变量 maxLen 记录最长回文子串的长度,初始值为 0。用一个变量 start 记录最长回文子串的起始位置,初始值为 0。
- 用一个循环遍历所有可能的子串长度 len,从 1 到 n。
- 对于每个 len,用另一个循环遍历所有可能的子串起始位置 i,从 0 到 n - len。
- 计算子串结束位置 j = i + len - 1。
- 根据 len 的不同,判断 s[i…j] 是否是回文串,并更新 dp[i][j] 的值。
- 如果 len == 1,那么 s[i…j] 只有一个字符,一定是回文串,所以 dp[i][j] = true。
- 如果 len == 2,那么 s[i…j] 只有两个字符,需要判断它们是否相等,所以 dp[i][j] = s.charAt(i) == s.charAt(j)。
- 如果 len > 2,那么 s[i…j] 有多个字符,需要判断首尾字符是否相等,并且去掉首尾字符后的子串是否是回文串,所以 dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]。
- 如果 dp[i][j] == true,并且 len > maxLen,说明找到了一个更长的回文子串,更新 maxLen 和 start 的值。
- 循环结束后,返回 s.substring(start, start + maxLen),这就是最长回文子串。
具体实现
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n]; // dp[i][j] 表示 s[i..j] 是否是回文串
int maxLen = 0; // 最长回文子串的长度
int start = 0; // 最长回文子串的起始位置
for (int len = 1; len <= n; len++) { // 遍历所有可能的子串长度
for (int i = 0; i < n - len + 1; i++) { // 遍历所有可能的子串起始位置
int j = i + len - 1; // 子串结束位置
if (len == 1) { // 长度为 1 的子串一定是回文串
dp[i][j] = true;
} else if (len == 2) { // 长度为 2 的子串需要判断两个字符是否相等
dp[i][j] = s.charAt(i) == s.charAt(j);
} else { // 长度大于 2 的子串需要判断两端字符是否相等,并且去掉两端字符后的子串是否是回文串
dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1];
}
if (dp[i][j] && len > maxLen) { // 如果当前子串是回文串,并且长度大于之前的最大长度,更新最大长度和起始位置
maxLen = len;
start = i;
}
}
}
return s.substring(start, start + maxLen); // 返回最长回文子串
}
}