【题目】 给定一个字符串 s,找到 s 中最长的 回文 子串。
示例 1:输入:s = "babad";输出:"bab";解释:"aba" 同样是符合题意的答案。
示例 2:输入:s = "cbbd";输出:"bb"
提示:1 <= s.length <= 1000;s 仅由数字和英文字母组成。
本文的分析参考自leetcode。
【方法】 中心扩展算法
class Solution {
public String longestPalindrome(String s) {
// 输入验证:首先检查输入字符串是否为空或长度小于 1,如果是则直接返回空字符串。
if (s == null || s.length() < 1) {
return "";
}
// 变量初始化:start和end用于记录最长回文子串的起始和结束位置。
int start = 0, end = 0;
// 对每个字符位置i,分别以i为中心(奇数长度回文)和以i与i+1为中心(偶数长度回文)调用expandAroundCenter方法。
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
// 比较两种情况下得到的回文长度,取最大值。
int len = Math.max(len1, len2);
// 如果当前回文长度大于之前记录的最长长度,则更新start和end。
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
// 对于奇数长度的回文,中心位置为i,回文长度为奇数len,通过上述公式可以正确计算出起始和结束位置。
// 对于偶数长度的回文,中心位置在i和i+1之间,回文长度为偶数len,同样可以通过上述公式正确计算出起始和结束位置。
}
// 返回结果:根据记录的起始和结束位置截取并返回最长回文子串。
return s.substring(start, end + 1);
}
// 功能:从给定的中心位置(left和right)开始,向两边扩展,直到无法形成回文为止,并返回回文的长度。
public int expandAroundCenter(String s, int left, int right) {
// 扩展逻辑:在满足左右边界有效且对应字符相等的条件下,不断将左指针左移,右指针右移。
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
--left;
++right;
}
// 长度计算:当循环结束时,left和right已经超出了回文的边界,因此回文的实际长度为right - left - 1。
return right - left - 1;
}
}
上面的解决方案采用了中心扩展法来寻找最长回文子串。
其核心思想是:遍历字符串中的每个字符,以该字符为中心(或该字符与其下一个字符为中心,处理偶数长度回文的情况),向两边扩展,尽可能地形成最长的回文子串。
下面进行复杂度分析。
时间复杂度:O (n²),其中 n 是字符串的长度。对于每个字符位置,最多需要扩展 n/2 次。
空间复杂度:O (1),只使用了常数级的额外空间。
下面是C++解决方案:
class Solution {
public:
// 从给定的中心位置(left和right)开始,向两边扩展,直到无法形成回文为止,并返回回文的左右边界。
pair<int, int> expandAroundCenter(const string& s, int left, int right) {
while (left >= 0 && right < s.size() && s[left] == s[right]) {
--left;
++right;
}
// 当循环结束时,left和right已经超出了回文的边界,因此实际的回文边界是left + 1和right - 1,函数返回这对边界值。
return {left + 1, right - 1};
}
string longestPalindrome(string s) {
int start = 0, end = 0;
for (int i = 0; i < s.size(); ++i) {
auto [left1, right1] = expandAroundCenter(s, i, i);
auto [left2, right2] = expandAroundCenter(s, i, i + 1);
if (right1 - left1 > end - start) {
start = left1;
end = right1;
}
if (right2 - left2 > end - start) {
start = left2;
end = right2;
}
}
return s.substr(start, end - start + 1);
}
};