Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
解题思路
依次以某一个字符或两个字符为中心点,向两边扩展,寻找最大回文子串。需要特别注意回文的字符是偶数个和奇数个的情况。
所以这里的寻找回文子串的函数 expandAroundCenter
有两个参数作为中心,分别处理上述两种情况。
class Solution {
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);
// 如果是以某个字符为中心的回文子串最长(len1),len一定是奇数(babad)
// 如果是以两个字符为中心的回文子串最长(len2),len一定是偶数(cbbd)
if (len > end - start + 1) {
// 0 1 2 3 4
// b a b a d
// i = 2时,得到的len = 3,start = 1, end = 3
// c b b d
// i = 1时,得到的len=2,start = 1, end = 2
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
/**
* 以left和right为中心,向两边扩展去找最长回文子串
*
* @param s
* @param left
* @param right
* @return
*/
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++;
}
// 这里因为最后一次循环L--了R++,所以最后的长度需要减1
return R - L - 1;
}
}
To improve over the brute force solution, we first observe how we can avoid unnecessary re-computation while validating palindromes. Consider the case "ababa". If we already knew that "bab" is a palindrome, it is obvious that "ababa" must be a palindrome since the two left and right end letters are the same.
This yields a straight forward DP solution, which we first initialize the one and two letters palindromes, and work our way up finding all three letters palindromes, and so on...
Complexity Analysis
- Time complexity : O(n^2). This gives us a runtime complexity of O(n^2).
- Space complexity : O(n^2). It uses O(n^2) space to store the table.
Additional Exercise Could you improve the above space complexity further and how?
i, j | 0(b) | 1(a) | 2(b) | 3(a) | 4(d) |
---|---|---|---|---|---|
0(b) | true | f | t | f | f |
1(a) | --- | true | f | t | f |
2(b) | --- | --- | true | f | f |
3(a) | --- | --- | --- | true | f |
4(d) | --- | --- | --- | --- | true |
如图所示, P(i, i)
肯定为 true
。
- 我们只需要判断
j > i
的情况。 - **对于
**i**
**j**
的遍历,一定是从**i: n -> 0**
**j: i -> n**
的顺序,因为判断**P(i, j)**
需要知道**P(i +1, j-1)**
的结果。如图所示**P(1, 4) = P(2, 3) and S_i == S_j**
,要先算出**P(2, 3)**
** - 需要特殊处理
P(i, i+1)
的情况,该情况下只要满足S_i == S_i+1
就为true
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int n = s.length();
int start = 0, end = 0;
boolean[][] p = new boolean[n][n];
// i从大到小,j从小到大遍历
for (int i = n - 2; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
// 这里并没有在一开始初始化p(i,i)的值为true,
// 而是直接在这里通过 j - i <= 2 的条件和P(i, i+1)的特殊情况一起处理了
if (s.charAt(i) == s.charAt(j) && (j - i <= 2 || p[i + 1][j - 1])) {
p[i][j] = true;
if (j - i + 1 > end - start + 1) {
start = i;
end = j;
}
}
}
}
return s.substring(start, end + 1);
}
}