/**
* 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.
*
* Tags: String
*/
class LongestPalindromicSubstring {
public static void main(String[] args) {
LongestPalindromicSubstring l = new LongestPalindromicSubstring();
String s = "abba";
System.out.println(l.longestPalindrome(s));
}
/**
* Manacher's Algorithm, O(n) Time.
* Insert a special character between each character in a string, and also both at the start and the end.
* S = “abba” => T = “#a#b#b#a#”.
* Expand from center
*/
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) return "";
int len = s.length();
int max = 0; // max length
String res = "";
for (int i = 1; i <= 2 * len - 1; i++) { // skip two #s
int count = 1;
while (i - count >= 0 && i + count <= 2 * len && get(s, i - count) == get(s, i + count)) count++;
count--; // there will be one extra count for the outbound #
if (count > max) { // update max and result when longer is found
res = s.substring((i - count) / 2, (i + count) / 2);
max = count;
}
}
return res;
}
/**
* Insert char to the original input string
* If the index is even, return #
* If the index is odd, return char in the original string
*/
private char get(String s, int i) {
if (i % 2 == 0) return '#';
else return s.charAt(i / 2);
}
/**
* Manacher's Algorithm, O(n) Time.
* S = “abba” => T = “^#a#b#b#a#$”.
* http://www.felix021.com/blog/read.php?2040
* http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
*/
public String longestPalindromeB(String s) {
String t = preProcess(s);
int n = t.length();
int[] p = new int[n];
int range = 0, center = 0;
for (int i = 1; i < n - 1; i++) {
int mirror = 2 * center - i; // mirror of i to center
p[i] = range > i ? Math.min(range - i, p[mirror]) : 0;
while (t.charAt(i + 1 + p[i]) == t.charAt(i - 1 - p[i])) p[i]++;
if (i + p[i] > range) {
center = i;
range = i + p[i];
}
}
int maxLen = 0;
int centerIdx = 0;
for (int i = 1; i < n - 1; i++) {
if (p[i] > maxLen) {
maxLen = p[i];
centerIdx = i;
}
}
return s.substring((centerIdx - 1 - maxLen) / 2, (centerIdx - 1 + maxLen) / 2);
}
private String preProcess(String s) {
int n = s.length();
if (n == 0) return "^$";
String res = "^";
for (int i = 0; i < n; i++) {
res += "#" + s.substring(i, i + 1);
}
res += "#$";
return res;
}
/**
* O(n^2) Time, O(1) Space
* Expand from center character and center of two chars
* Update result according to the returned length
*/
public String longestPalindromeC(String s) {
if (s == null || s.length() == 0) return "";
String longest = s.substring(0, 1);
int len = s.length();
for (int i = 0; i < len - 1; i++) {
String s1 = expandAroundCenter(s, i, i); // expand from one char
if (s1.length() > longest.length()) longest = s1;
String s2 = expandAroundCenter(s, i, i + 1); // expand from two chars
if (s2.length() > longest.length()) longest = s2;
}
return longest;
}
/**
* Search for range in both direction
*/
private String expandAroundCenter(String s, int i, int j) {
int l = i;
int r = j;
int n = s.length();
while (l >= 0 && r <= n - 1 && s.charAt(l) == s.charAt(r)) {
l--;
r++;
}
return s.substring(l + 1, r); // note the range is from l + 1 to r - 1
}
}