一、题目描述
给你一个字符串 s,找出 s 中最长的回文子串。
示例:
- 示例 1:
- 输入:
s = "babad" - 输出:
"bab" - 解释:
"aba"同样也是符合题意的答案。
- 输入:
- 示例 2:
- 输入:
s = "cbbd" - 输出:
"bb"
- 输入:
- 示例 3:
- 输入:
s = "a" - 输出:
"a"
- 输入:
- 示例 4:
- 输入:
s = "ac" - 输出:
"a"或"c"
- 输入:
二、解题思路
1. 暴力解法
暴力解法是最直接的思路,通过枚举字符串的所有子串,然后判断每个子串是否为回文串,记录下最长的回文子串。
- 枚举所有子串:使用两层循环,外层循环从字符串的第一个字符开始,到最后一个字符结束,确定子串的起始位置
i。内层循环从i开始,到字符串末尾结束,确定子串的结束位置j,这样就得到了所有可能的子串s[i:j + 1]。 - 判断子串是否为回文串:对于每个子串,通过比较子串的前半部分和后半部分是否相等来判断是否为回文串。可以使用双指针法,一个指针指向子串的开头,一个指针指向子串的末尾,向中间移动指针并比较对应字符是否相等。如果所有对应字符都相等,则该子串是回文串。
2. 中心扩展算法
回文串具有中心对称的特点,因此可以从字符串的每个字符或相邻两个字符为中心,向两边扩展,寻找最长的回文子串。
- 单字符中心:对于字符串中的每个字符
s[i],可以将其作为回文串的中心,向两边扩展,即比较s[i - 1]和s[i + 1],s[i - 2]和s[i + 2]等等,直到两边字符不相等为止。 - 双字符中心:对于相邻的两个字符
s[i]和s[i + 1],如果它们相等,那么可以将这两个字符作为回文串的中心,同样向两边扩展,比较s[i - 1]和s[i + 2],s[i - 2]和s[i + 3]等等,直到两边字符不相等。 - 记录最长回文子串:在扩展过程中,记录下每次找到的回文子串的长度和起始位置,最后根据记录的信息得到最长的回文子串。
三、代码实现
暴力解法(Python)
def longestPalindrome_brute(s):
max_length = 0
start = 0
for i in range(len(s)):
for j in range(i, len(s)):
is_palindrome = True
for k in range((j - i + 1) // 2):
if s[i + k]!= s[j - k]:
is_palindrome = False
break
if is_palindrome and (j - i + 1) > max_length:
max_length = j - i + 1
start = i
return s[start:start + max_length]
中心扩展算法(Python)
def longestPalindrome(s):
def expand_around_center(left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return right - left - 1
start = 0
max_length = 0
for i in range(len(s)):
length1 = expand_around_center(i, i)
length2 = expand_around_center(i, i + 1)
max_length = max(max_length, length1, length2)
if max_length > (right - left + 1):
if max_length == length1:
start = i - (max_length - 1) // 2
else:
start = i - max_length // 2 + 1
return s[start:start + max_length]
暴力解法(Java)
public class LongestPalindromicSubstringBrute {
public String longestPalindrome(String s) {
int maxLength = 0;
int start = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i; j < s.length(); j++) {
boolean isPalindrome = true;
for (int k = 0; k < (j - i + 1) / 2; k++) {
if (s.charAt(i + k)!= s.charAt(j - k)) {
isPalindrome = false;
break;
}
}
if (isPalindrome && (j - i + 1) > maxLength) {
maxLength = j - i + 1;
start = i;
}
}
}
return s.substring(start, start + maxLength);
}
}
中心扩展算法(Java)
public class LongestPalindromicSubstring {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
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);
if (len > end - start + 1) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
return right - left - 1;
}
}
四、复杂度分析
暴力解法
- 时间复杂度:O(n3)O(n^3)O(n3),其中 nnn 是字符串的长度。两层循环用于枚举所有子串,时间复杂度为 O(n2)O(n^2)O(n2),对于每个子串判断是否为回文串,时间复杂度为 O(n)O(n)O(n),所以总的时间复杂度为 O(n3)O(n^3)O(n3)。
- 空间复杂度:O(1)O(1)O(1),除了存储结果的空间外,只使用了常数级别的额外空间。
中心扩展算法
- 时间复杂度:O(n2)O(n^2)O(n2),其中 nnn 是字符串的长度。对于字符串中的每个字符,最多需要向两边扩展 O(n)O(n)O(n) 次,所以总的时间复杂度为 O(n2)O(n^2)O(n2)。
- 空间复杂度:O(1)O(1)O(1),只使用了常数级别的额外空间。
五、总结
“最长回文子串”问题通过暴力解法和中心扩展算法展示了不同的解题思路。暴力解法虽然直观但效率较低,中心扩展算法利用回文串的特性,显著提高了效率。在解决字符串相关问题时,深入理解字符串的特性并合理利用这些特性,能够设计出更高效的算法。希望通过对本题的学习,能增强你对字符串算法的理解和应用能力。

2154

被折叠的 条评论
为什么被折叠?



