题目
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example :
Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
我的想法
从中间向外扩散,使用半径来扩散,只考虑到了奇数子串的对称。解决这个问题可以参考下面Manacher算法
class Solution {
public String longestPalindrome(String s) {
if(s.length() == 0 ||s.length() == 1) return s;
int center = 1, lenMax = 0;
for(int i = 1; i < s.length()-1; i++){
int radius = 1, length = 0;
while(i - radius >= 0 && i + radius < s.length()){
if(s.charAt(i - radius) == s.charAt(i + radius)){
length = radius;
radius ++;
}
else{
radius--;
length = radius;
break;
}
}
if(length > lenMax){
center = i;
lenMax = length;
}
}
return s.substring(center-lenMax, center+lenMax);
}
}
2019.9.13 update: 再刷这道题时能想起可以用中心线和dp的方法来做,但是中心线却怎么也写不出来。所以还是不熟练,没有吃透
解答
leetcode solution 1: Expand Around Center
也是从中间向外扩散,用边界进行扩散,兼顾了奇数和偶数子串。时间复杂度为n*n
2019.9.13 update: 外层循环控制的是中心线!!!不是起点!!!!换个名字更好记忆,枚举中心点。这里特别还需注意头和尾的表达方式
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);//奇数,中心为一个确切的数
int len2 = expandAroundCenter(s, i, i + 1);//偶数,中心在两个数之间
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2; //特别注意头尾的换算方法
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1; //(R-1)-(L+1)+1 循环结束后的R和L已经改变,需要改回来
}
leetcode solution 2: Dynamic Programming
判断dp[i][j]需先判断dp[i+1][j-1]是否为true,再判断s.i==s.j
性能没有方法一好
public String longestPalindrome(String s) {
int n = s.length();
String res = null;
boolean[][] dp = new boolean[n][n];
//2019.9.13update: 需要注意,因为这里也是bottom-up,没有用递归
//因此必须从后往前循环,这样才能保证前项的cases都已经计算完成
for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 3 || dp[i + 1][j - 1]);
if (dp[i][j] && (res == null || j - i + 1 > res.length())) {
res = s.substring(i, j + 1);
}
}
}
return res;
}
2019.9.13update: 第二种写法bottom-up,应该比第一种更加清晰。外层为子串长度,内层循环起始数字
public String longestPalindrome(String s) {
// write your code here
if(s == null || s.length() <= 1) return s;
boolean[][] dp = new boolean[s.length()][s.length()];
int start = 0, end = 0;
for(int len = 0; len < s.length(); len++){
for(int i = 0; i + len < s.length(); i++) {
if((len < 2 || dp[i + 1][i + len - 1]) && s.charAt(i) == s.charAt(i + len)) {
dp[i][i + len] = true;
if(len > end - start) {
start = i;
end = i + len;
}
}
}
}
return s.substring(start, end+1); //注意substring第一个参数inclusive,第二个exclusive
}
leetcode solution 3: Manacher’s Algorithm
通过补符号使得字符串永为奇数串
bob --> #b#o#b#
noon --> #n#o#o#n#
class Solution {
public String longestPalindrome(String s) {
char[] c = new char[s.length() * 2 + 3];
c[0] = '^';
c[1] = '@';
for (int i = 0; i < s.length(); i++) {
c[2 + i * 2] = s.charAt(i);
c[2 + i * 2 + 1] = '@';
}
c[c.length - 1] = '$';
int[] length = new int[c.length];
int center = 0, right = 0, maxLeft = 0, maxLength = 0;
for (int i = 2; i < c.length - 2; i++) {
if (i < right) length[i] = Math.min(right - i, length[2 * center - i]);
while (c[i - length[i] - 1] == c[i + length[i] + 1]) length[i]++;
if (i > right) {
center = i;
right = i + length[i];
}
if (length[i] > maxLength) {
maxLength = length[i];
maxLeft = (i - length[i]) / 2;
}
}
return s.substring(maxLeft, maxLeft + maxLength);
}
}
leetcode solution 4: Suffix Tree
好难,之后学到树再补吧