https://www.lintcode.com/problem/longest-palindromic-substring/
Description
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.
Example
Example 1: Input:“abcdzdcab” Output:“cdzdc”
Example 2: Input:“aba” Output:“aba”
Challenge
O(n2) time is acceptable. Can you do it in O(n) time.
解:-------------------------------
在思考解法之前 (设):
1 回文的定义:
aba : 属于回文
aa : 属于回文
aaa : 属于回文
Aa : 不属于回文
会不会存在空格以及标点 : 不存在
2 input 与output
假定 input 为长度不超过1K的String。
返回也为String。
思路1:
整体循环1遍(2遍)假设每个字符都是回文串的中点。
从每一个字符开始, 当字符index = i。作 input.charAt(i+1) 与 input.charAt(i-1)的比较。如果直到溢出input的两边。
以上的方法只能算"aba"型的回文。
对于"aaaa"型的回文。
需要比较input.charAt(i+1) 与 input.charAt(i) ->input.charAt(i+2) 与 input.charAt(i-1)
此方法的时间复杂度为O(n^2).
思路2:
马拉车(Manacher)算法:
步骤1:先用一个小trick 将 aba型与aa型回文都变成单数size的回文
aba -> #a#b#a# (3->7)
aa -> #a#a# (2->5)
步骤二:
新建一个int[] 储存新String中,以当前位为中点的回文半径长度。
用遍历一遍, 挨个假设中点的方法遍历新的string
example:
#a#b#a#
1214121
#a#a#
12321
结论:最长回文即为int[] array 中最大的数字减一
至此 时间复杂度还是O(n^2)
步骤三: (关键)此剪枝方法可将复杂度降为O(n)
1 新建两个int 分别记录 当前最长回文的中点index(mid) 以及当前最长回文的半径(longest)
由此我们可以得出当前最长回文的右端点为 mid+longest.
(这里我们再计算一个点。i 以mid 为中点的对称点 j :j = 2*mid -i)
2 当主for loop的index i 小于最长回文的右端点时我们可以判定:i 以及i的回文长度 至少是 Math.min(j的回文长度, (mid+longest) - i) (这个长度我们称之为len,len的默认值为1)
3 在子loop中 我们可以从半径为len的位置开始进行。这样就达到了部分剪枝的效果。
此方法的时间复杂度为O(n).
思路1 解法
public class Solution {
/**
* @param s: input string
* @return: the longest palindromic substring
*/
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return s;
}
int max = 1;
String maxString = s.substring(0,1);
int length = s.length();
for (int i = 0; i < length; i++) {
int dis = Math.min(i, length-1-i);
System.out.println(dis);
if (2*dis < max -1) {
break;
}
for (int j = 0; j <= dis; j++) {
if (s.charAt(i+j) != s.charAt(i-j) ) {
break;
}
if (s.charAt(i+j) == s.charAt(i-j)) {
if (max < 2*j + 1) {
max = 2*j + 1;
maxString = s.substring(i-j, i+j+1);
}
}
}
for (int j = 0; j <= dis; j++) {
if ((i+j + 1) >= length || s.charAt(i+j +1) != s.charAt(i-j)) {
break;
}
if ((i+j + 1) < length && s.charAt(i+j + 1) == s.charAt(i-j)) {
if (max < 2*j + 2) {
max = 2*j + 2;
maxString = s.substring(i-j, i+j + 2);
}
}
}
}
return maxString;
// write your code here
}
}
思路2 解法
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;
// System.out.println("i = " + i);
// System.out.println("mid = " + mid);
// System.out.println("longest = " +longest);
if (mid + longest > i) {
int mirrorOfI = mid - (i - mid);
len = Math.min(palindrome[mirrorOfI], mid + longest - i);
}
// System.out.println("len = " +len);
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();
}
}