题目:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
输入: "cbbd"
输出: "bb"
思路:
- 背向双指针,遍历每一个索引,以这个索引为中心,利用“回文串”中心对称的特点,往两边扩散,看最多能扩散多远。
- 奇数回文串的“中心”是一个具体的字符,例如:回文串 “aba” 的中心是字符 “b”;
- 偶数回文串的“中心”是位于中间的两个字符的“空隙”,例如:回文串串 “abba” 的中心是两个 “b” 中间的那个“空隙”。
注意:String.substring(beginIndex, endIndex)
返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。
- Manancher’s Algorithm,有点复杂,据说面试不考,还没有看懂。
代码:
- 背向双指针
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) {
//对于奇数长度的字符串,中心为一个数,左右起点都是i
len = findLongestPalindrome(s, i, i);
if (len > longest) {
longest = len;
start = i - len / 2;
}
//对于偶数长度的字符串,中心是两数之间,左右起点不一样
len = findLongestPalindrome(s, i, i + 1);
if (len > longest) {
longest = len;
start = i - len / 2 + 1;
}
}
return s.substring(start, start + longest);
}
//找到指定了左右起点往两边扩散的最长回文子串长度
public int findLongestPalindrome(String s, int left, int right) {
int len = 0;
while (left >= 0 && right < s.length()) {
//如果左右扩散的两个值不一样,则说明已经不是回文串
if (s.charAt(left) != s.charAt(right)) {
break;
}
//左右起点归一时长度只增加1,否则加2
len += (left == right ? 1 : 2);
left--;
right++;
}
return len;
}
}
- 使用 Manancher’s Algorithm,可以在 O(n) 的时间内解决问题
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();
}
}