Description
给出一个字符串(假设长度最长为1000),求出它的最长回文子串,你可以假定只有一个满足条件的最长回文串。
样例
给出字符串 "abcdzdcab"
,它的最长回文子串为 "cdzdc"
。
挑战
O(n2) 时间复杂度的算法是可以接受的,如果你能用 O(n) 的算法那自然更好。
Solution
这道题一共有五种解法:
- Manacher's Algorithm - O(n) -----可以背诵
- 后缀数组 Suffix Array - O(nlogn)
- 动态规划 Dynamic Programming - O(n^2)
- 枚举法 Enumeration - O(n^2) ------推荐
- 暴力解法 Brute-force - O(n^3)
有关Manacher's Algorithm的方法可以参考这个链接:最长回文子串——Manacher 算法
下面使用 4. 基于中心点枚举的方法,时间复杂度O(n^2)
方法:由于回文子串的对称性,且整个字符串中的所有字符,以及字符间的空隙,都可能是某个回文子串的对称轴位置。可以遍历这些位置,在每个位置上同时向左和向右扩展,直到左右两边的字符不同,或者达到边界。且分为两种情况,一个是从一点为中心向两边扩展,一个是以两点的中间间隔为中心,向两边扩展。
public class Solution {
/**
* @param s: input string
* @return: the longest palindromic substring
*/
public String longestPalindrome(String s) {
// write your code here
//基于中心点枚举的方法,时间复杂度O(n^2)
if (s == null || s.length() == 0) {
return "";
}
int start = 0, len = 0, longest = 0;
for (int i = 0; i < s.length(); i++) {
//以一个点为中心点,向两边查找对称的最长子串个数
len = findLongestPalindromeFrom(s, i, i);
if (len > longest) {
longest = len;
start = i - len / 2; //起始位置为中心点减一半的对称长度
}
//以两个点的为中心,向两边查找对称的最长子串个数
len = findLongestPalindromeFrom(s, i, i + 1);
if (len > longest) {
longest = len;
start = i - len / 2 + 1; //起始位置为中心点减一半的对称长度加一
}
}
return s.substring(start, start + longest);
}
private int findLongestPalindromeFrom(String s, int left, int right) {
int len = 0;
//向两边寻找对称的最长子串个数
while (left >= 0 && right < s.length()) {
if (s.charAt(left) != s.charAt(right)) {
break;
}
if (left == right) { //左右位置相同时,长度只加1,其他加2
len += 1;
} else {
len += 2;
}
left--;
right++;
}
return len;
}
}