📅  最后修改于: 2023-12-03 15:26:05.238000             🧑  作者: Mango
本题涉及到一些计算机科学中常用的数据结构和算法,需要多方面的知识储备和实践经验。
给定一个字符串,找到其中最长的回文子串。例如,在输入字符串"babad"中,最长的回文子串是"bab"或"aba"。在输入字符串"cbbd"中,最长的回文子串是"bb"。
我们可以枚举所有的子串,判断其是否为回文串,并记录其中最长的。
暴力法的时间复杂度为$O(n^3)$,空间复杂度是$O(1)$。
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// 枚举所有长度大于等于 2 的子串
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
if (j - i + 1 > maxLen && validPalindromic(s, i, j)) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
private boolean validPalindromic(String s, int left, int right) {
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
回文串是一个中心对称的字符串,我们可以枚举可能成为回文串中心的位置,然后从中心向两边扩展,寻找回文串。
中心扩展算法的时间复杂度为$O(n^2)$,空间复杂度为$O(1)$。
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// 枚举所有可能成为回文中心的位置
for (int i = 0; i < len - 1; i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int curMaxLen = Math.max(len1, len2);
if (curMaxLen > maxLen) {
maxLen = curMaxLen;
begin = i - (curMaxLen - 1) / 2;
}
}
return s.substring(begin, begin + maxLen);
}
private int expandAroundCenter(String s, int left, int right) {
int len = s.length();
while (left >= 0 && right < len && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
// 返回以left和right为中心的最长回文串的长度
return right - left - 1;
}
Manacher算法是对中心扩展算法的优化,它利用已经计算出的回文串的信息,减少了不必要的计算。
Manacher算法的时间复杂度为$O(n)$,空间复杂度为$O(n)$。
public String longestPalindrome(String s) {
char[] chars = preprocessing(s);
int len = chars.length;
int[] p = new int[len];
int center = 0; // 当前回文中心
int mostRight = 0; // 当前回文中心最右边的坐标
int maxLen = 0;
int start = 0;
for (int i = 0; i < len; i++) {
// 初始化p[i]的值
if (mostRight > i) {
p[i] = Math.min(p[2 * center - i], mostRight - i);
} else {
p[i] = 1;
}
// 尝试扩展以i为中心的回文串
while (i - p[i] >= 0 && i + p[i] < len && chars[i - p[i]] == chars[i + p[i]]) {
p[i]++;
}
// 判断当前回文串是否比之前的最长回文串更长
if (i + p[i] > mostRight) {
center = i;
mostRight = i + p[i];
}
if (p[i] > maxLen) {
maxLen = p[i];
start = (i - maxLen) / 2;
}
}
return s.substring(start, start + maxLen - 1);
}
private char[] preprocessing(String s) {
int len = s.length();
char[] chars = new char[len * 2 + 1];
int j = 0;
for (int i = 0; i < len; i++) {
chars[j++] = '#';
chars[j++] = s.charAt(i);
}
chars[j] = '#';
return chars;
}
本题涉及到的解法主要是暴力法、中心扩展法和Manacher算法。其中,Manacher算法是时间与空间上最优的解法。需要注意,对于一些边界情况,可能需要特殊处理。