Description
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Examples
Example1
Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
Example2
Input: “cbbd”
Output: “bb”
解题思路
如果要分奇偶情况讨论很麻烦,所以在字符串中每两个字符之间都加上分隔符#,这样无论回文字数是奇数还是偶数都可以统一处理
以题目给出的例子写一下
“babad” -> “#b#a#b#a#d#”
- 回文子串bab以a作为center,半径为3
“cbbd” -> “#c#b#b#d#”
- 回文子串bb以#作为center,半径为2
接下来的事情就很简单了,对更新之后的字符串进行遍历,对每个索引点检测以它为center的回文串长度,如果比现有最长的长,就更新max_length和start_pos
java代码如下:
class Solution {
public String longestPalindrome(String s) {
int i, j, pos_start = 0, max_len = 0;
String s_temp = "#";
for(i = 0; i < s.length(); i++){
s_temp += s.charAt(i);
s_temp += "#";
}
for(i = 0; i < s_temp.length(); i++){
for(j = 0; j <= i && j < s_temp.length() - i; j++){
if(s_temp.charAt(i - j) != s_temp.charAt(i + j))
break;
}
if(j > max_len){
max_len = j - 1;
pos_start = i;
}
}
String temp = "";
for(i = pos_start - max_len; i <= pos_start + max_len; i++){
if(s_temp.charAt(i) != '#')
temp += s_temp.charAt(i);
}
return temp;
}
}
Manacher’s algorithm
在leetcode给出的源码中,有一个Manacher’s算法,(其实大概也许可能没怎么看懂,先把自己的理解记录一下
首先他的预处理与我之前做的一样,但他同时新建了一个存储以该点为中心的最长对称长度的数组,举个例子
S1 = ababa
T1 = # a # b # a # b # a #
P1 = 0 1 0 3 0 5 0 3 0 1 0
T的构造很简单,在得到了数组P之后找到回文子串也很方便,现在的问题就是如何得到数组P
如下,现在要求P[9]
T = # b # a # b # c # b # a # b # c # b # a # c # c # b # a #
P = 0 1 0 3 0 1 0 7 0 ? …
很容易得到如果以T[7]为center的子串radius为7(包含了T[9]),则P[7 + 2] = P[7 - 2]这个结论
现在来看T[9] = T[5] = 1这个结论好像是正确的
但之后就会有错
T = # b # a # b # c # b # a # b # c # b # a # c # c # b # a #
P = 0 1 0 3 0 1 0 7 0 1 0 ? …
如果按照上面的方法来推理的话,这里P[7 + 4]属于7 + 7的范围,所以应该等于P[7 - 4] = 3
但按照全局来看,它应该等于9
这就和之前找到的规律相悖了
所以产生了一个新规则
可以看到上面的例子中P[7 + 4]被center P[7]罩着,与之对应的P[7 - 4] = 3,那也就是说,以center = P[3]罩着的数中有center P[7]罩不到的,那就不能做单纯的复制工作了
但我们依然可以确保在center P[7]罩得到的地方,center P[3]回文等于center P[11]的回文,也就是说,对于P[11]不用从11开始计算,从7 + P[7] 开始算起就可以了
那总结一下
我们假设当前要计算的点是i,与之对应的center是center,位于center另一头的是mirror
- 当i > center + center_radius的时候,从零开始计算
- 当P[mirror] < P[center] - (i - center)的时候,直接复制P[mirror]的值
# x # x # x # x # x # x # x # x # x # x # x # x # x # x # x # x #
假设加粗部分为mirror罩着的位置,黄色部分为center罩着的数字 - 当i
≤
\le
≤ center + P[center] 且P[mirror] > P[center] - (i - center)的时候,从P[center] + center + 1开始计算回文
# x # x # x # x # x # x # x # x # x # x # x # x # x # x # x # x #
# x # x # x # x # x # x # x # x # x # x # x # x # x # x # x # x #
假设同上
java代码如下:
string preProcess(string s) {
int n = s.length();
if (n == 0) return "^$";
string ret = "^";
for (int i = 0; i < n; i++)
ret += "#" + s.substr(i, 1);
ret += "#$";
return ret;
}
string longestPalindrome(string s) {
string T = preProcess(s);
int n = T.length();
int *P = new int[n];
int C = 0, R = 0;
for (int i = 1; i < n-1; i++) {
int i_mirror = 2*C-i;
P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0;
while (T[i + 1 + P[i]] == T[i - 1 - P[i]])
P[i]++;
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
}
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n-1; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
delete[] P;
return s.substr((centerIndex - 1 - maxLen)/2, maxLen);
}