这道题目真是弄了很久啊,然后查了好多相关知识,哎,最近貌似一直很焦躁,我这是怎么了,不能继续这么下去啊,加油!
英文原文地址:http://leetcode.com/2011/11/longest-palindromic-substring-part-i.html
1.回文简介
首先,介绍下回文的含义。回文就是对称的序列,无论从前往后读,还是从后往前读,都是一样的。例如,“aba”是回文,而"abc"不是回文。
2.很容易犯的一个错误:
很多人都会快速提出这么一个有漏洞的解决方案: 逆转字符串序列S为S',然后寻找S和S'的最长公共子串(longest common substring),即最长相同回文子串。
这看起来是正确的,但是却存在漏洞。让我们来用几个例子验证下,S="caba",S'="abac" ,其最长公共子串为"aba",答案正确。让我们试下另外一个例子,S = “abacdfgdcaba”, S’ = “abacdgfdcaba”,其最长公共子串为“abacd”。显然,这不是一个回文。我们可以看到,这个方法的问题出在存在非回文的公共子串。正确的方法是,当找到一个最长公共子串时,检查其索引值是否正确,如果正确,就将其设置为最长回文子串,否则就跳过,寻找下一个最长回文子串。
显然,这个算法是动态规划的思想,空间复杂度和时间复杂度都是O(n^2)。
3.暴力解法
4.动态规划算法
public class Solution {
public String longestPalindrome(String s) {
int n = s.length();
int longestBegin = 0;
int maxLen = 1;
boolean table[][] = new boolean[1000][1000];
for (int i = 0; i < n; i++) {
table[i][i] = true;
}
for (int i = 0; i < n-1; i++) {
if (s.charAt(i) == s.charAt(i+1)) {
table[i][i+1] = true;
longestBegin = i;
maxLen = 2;
}
}
for (int len = 3; len <= n; len++) {
for (int i = 0; i < n-len+1; i++) {
int j = i+len-1;
if (s.charAt(i) == s.charAt(j) && table[i+1][j-1]) {
table[i][j] = true;
longestBegin = i;
maxLen = len;
}
}
}
return s.substring(longestBegin, longestBegin+maxLen);
}
}
5.简单方法,时间复杂度为O(n^2),空间复杂度O(1)
public class Solution {
public String expandAroundCenter(String s, int c1, int c2) {
int l = c1, r = c2;
int n = s.length();
while (l >= 0 && r <= n-1 && s.charAt(l) == s.charAt(r)) {
l--;
r++;
}
return s.substring(l+1, r);
}
public String longestPalindrome(String s) {
int n = s.length();
if (n == 0) return "";
String longest = s.substring(0, 1); // a single char itself is a palindrome
for (int i = 0; i < n-1; i++) {
String p1 = expandAroundCenter(s, i, i);
if (p1.length() > longest.length())
longest = p1;
String p2 = expandAroundCenter(s, i, i+1);
if (p2.length() > longest.length())
longest = p2;
}
return longest;
}
}
想想如何简化算法的复杂度。考虑最坏的情况,即输入字符串包含多个回文,且回文之间存在覆盖现象。例如,“aaaaaaaaa”和“cabcbabcbabcba”。事实上,我们可以利用回文对称的性质来避免一些不必要的计算。
6.一个时间复杂度为O(n)的解决方案(Manache算法)
public class Solution {
// Transform S into T.
// For example, S = "abba", T = "^#a#b#b#a#$".
// ^ and $ signs are sentinels appended to each end to avoid bounds checking
public String preProcess(String s) {
int n = s.length();
if (n == 0) return "^$";
String ret = "^";
for (int i = 0; i < n; i++)
ret += "#" + s.charAt(i);
ret += "#$";
return ret;
}
public String longestPalindrome(String s) {
String T = preProcess(s);
int n = T.length();
int[] P = new int[n];
int C = 1, R = 1;
for (int i = 1; i < n-1; i++) {
int i_mirror = 2*C-i; // equals to i' = C - (i-C)
P[i] = Math.min(R-i, P[i_mirror]);
// Attempt to expand palindrome centered at i
while (T.charAt(i + 1 + P[i]) == T.charAt(i - 1 - P[i]))
P[i]++;
// If palindrome centered at i expand past R,
// adjust center based on expanded palindrome.
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
}
// Find the maximum element in P.
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n-1; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
return s.substring((centerIndex - 1 - maxLen)/2, (centerIndex - 1 - maxLen)/2+maxLen);
}
}