Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
最长回文子串Longest palindromic substring, 最长回文子串或最长对称因子问题是在一个字符串中查找一个最长连续子串,这个子串必须是回文。例如“banana”最长回文子串是“anana”。最长回文子串算法不应当与最长回文子序列算法混淆。
1. 枚举(enumerate): 基于中心点枚举的算法, 遍历数组,以其中的1个元素或者2个元素作为palindrome的中心,通过辅助函数,寻找能拓展得到的最长子字符串。外层循环 O(n),内层循环O(n),因此时间复杂度 Time O(n^2),因为只需要存最长palindrome子字符串本身,空间更优化:Space O(1)
2. 动态归化(DP):
State: dp[i][j], s[i]到s[j]是否构成一个回文
Function: dp[i][j] = s[i] == s[j] &&(j - i <= 2 || dp[i + 1][j - 1])
Initialize: dp[i][j] = true when j - i <= 2
Return: longest substring: longest = s[i, j + 1]
3. Manacher‘s Algorithm,可以达到 O(n) 的线性时间复杂度。
Manacher算法可参考:
Manacher‘s ALGORITHM: O(n)时间求字符串的最长回文子串
Longest Palindromic Substring Part II
Java: 枚举(enumerate), Time: O(n^2), Space: O(1)
public class Solution {
public String longestPalindrome(String s) {
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;
}
len += left == right ? 1 : 2;
left--;
right++;
}
return len;
}
}
Java: DP, Time: O(n^2), Space: O(n^2)
public class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return "";
}
int n = s.length();
boolean[][] isPalindrome = new boolean[n][n];
int longest = 1, start = 0;
for (int i = 0; i < n; i++) {
isPalindrome[i][i] = true;
}
for (int i = 0; i < n - 1; i++) {
isPalindrome[i][i + 1] = s.charAt(i) == s.charAt(i + 1);
if (isPalindrome[i][i + 1]) {
start = i;
longest = 2;
}
}
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 2; j < n; j++) {
isPalindrome[i][j] = isPalindrome[i + 1][j - 1] &&
s.charAt(i) == s.charAt(j);
if (isPalindrome[i][j] && j - i + 1 > longest) {
start = i;
longest = j - i + 1;
}
}
}
return s.substring(start, start + longest);
}
}
Java: Manacher‘s Algorithm
public class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return "";
}
// abc => #a#b#c#
String str = generateString(s);
int[] palindrome = new int[str.length()];
int mid = 0, longest = 1;
palindrome[0] = 1;
for (int i = 1; i < str.length(); i++) {
int len = 1;
if (mid + longest > i) {
int mirrorOfI = mid - (i - mid);
len = Math.min(palindrome[mirrorOfI], mid + longest - i);
}
while (i + len < str.length() && i - len >= 0) {
if (str.charAt(i - len) != str.charAt(i + len)) {
break;
}
len++;
}
if (len > longest) {
longest = len;
mid = i;
}
palindrome[i] = len;
}
longest = longest - 1; // remove the extra #
int start = (mid - 1) / 2 - (longest - 1) / 2;
return s.substring(start, start + longest);
}
private String generateString(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
sb.append(‘#‘);
sb.append(s.charAt(i));
}
sb.append(‘#‘);
return sb.toString();
}
}