welcome to my blog
LeetCode Top 100 Liked Questions 5.Longest Palindromic Substring (Java版; Medium)
题目描述
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"
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
if(n==0){
return "";
}
//dp[i][j]表示s[i,j]是否是回文子串
//dp[i][j] = dp[i+1][j-1] && s[i]==s[j] else
//dp[i][j] = s[i]==s[j] if i+1==j
boolean[][] dp = new boolean[n][n];
//初始化有讲究
int[] tmp = {0,1};
for(int i=0; i<n; i++){
dp[i][i] = true;
}
for(int i=n-1; i>=0; i--){
for(int j=i+1; j<n; j++){
if(i+1==j){
dp[i][j] = s.charAt(i)==s.charAt(j);
}else{
dp[i][j] = dp[i+1][j-1] && s.charAt(i)==s.charAt(j);
}
if(dp[i][j] && j-i+1 > tmp[1]){
tmp[1] = j - i + 1;
tmp[0] = i;
}
}
}
return s.substring(tmp[0], tmp[0]+tmp[1]);
}
}
第二次做; 动态规划 核心: 1)dp[i][j]表示s[i,j]是否是回文子串 2)i相当于左边界, j相当于右边界, 左边界不能超过右边界 3)递推公式见注释 4)根据依赖关系可知, 需要从下往上, 从左往右遍历 5)先保存区间, 最后再用substring, 节省空间
class Solution {
public String longestPalindrome(String s) {
//input check
if(s==null || s.length()<=1){
return s;
}
/*
dp[i][j]表示s[i,j]是不是回文子串;
i相当于左边界, j相当于右边界, i<=j
dp[i][j] = s[i]==s[j] && (j-i<=2 || dp[i+1][j-1])
*/
int n = s.length();
boolean[][] dp = new boolean[n][n];
//res[0]表示左边界, res[1]表示右边界, res[2]表示最大长度
int[] res = {0,0,0};
for(int i=n-1; i>=0; i--){
for(int j=i+1; j<n; j++){
dp[i][j] = s.charAt(i)==s.charAt(j) && (j-i<=2 || dp[i+1][j-1]);
if(dp[i][j] && j-i+1>res[2]){
res[2] = j-i+1;
res[0] = i;
res[1] = j;
}
}
}
return s.substring(res[0], res[1]+1);
}
}
第二次做; 中心扩展法
class Solution {
public String longestPalindrome(String s) {
//input check
if (s == null || s.length() <= 1) {
return s;
}
//记录回文串的左右边界
int left = 0, right = 0;
int n = s.length();
for (int i = 0; i < n; i++) {
int len1 = expand(s, i, i);
int len2 = expand(s, i, i + 1);
if (len1 > len2 && len1 > right - left + 1) {
left = i - len1 / 2;
right = i + len1 / 2;
} else if (len2 > len1 && len2 > right - left + 1) {
left = i - (len2 - 2) / 2;
right = i+1 + (len2 - 2) / 2;
}
}
return s.substring(left, right + 1);
}
private int expand(String s, int x, int y) {
int left = x, right = y;
while (x >= 0 && y < s.length() && s.charAt(x) == s.charAt(y)) {
x--;
y++;
}
return y - x - 1;
}
}
第一次做, 统一长度的更新公式; 调整了for循环条件, while循环条件
class Solution {
public String longestPalindrome(String s) {
if(s==null || s.length()<2)
return s;
int left = 0, right = 0;
int len1 = 0, len2 = 0;
//比起上一次修改了i的终止条件
for(int i=0; i<s.length(); i++){
len1 = centerExpand(s, i, i);
len2 = centerExpand(s, i, i+1);
len1 = Math.max(len1, len2);
if(len1 > right - left + 1){
left = i - (len1 - 1)/2;
right = i + len1/2;
}
}
return s.substring(left, right+1);
}
public int centerExpand(String s, int i, int j){
int left=i, right=j;
while(left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){
left--;
right++;
}
return right - left - 1;
}
}
第一次做, 使用中心扩展法
- 中心可能是同一个字符, 也可能是相邻的两个字符;
- 字符串长度小于2时直接返回;
- 注意循环终止条件, s至少有两个字符才会进入循环
- 长度一次增加2
class Solution {
public String longestPalindrome(String s) {
//字符串长度小于2时直接返回
if(s==null || s.length()<2)
return s;
String curr = "";
int len1, len2;
int left, right;
//注意循环终止条件, s至少有两个字符才会进入循环
for(int i=0; i<s.length()-1; i++){
len1 = centerExpand(s, i, i);
len2 = centerExpand(s, i, i+1);
if(len1 > len2){
left = i - (len1 - 1)/2;
right = i + (len1 -1)/2; //可以写成i + len1/2
curr = len1 > curr.length() ? s.substring(left, right+1) : curr;
}
else{
left = i - (len2-2)/2; //可以写成i-(len2-1)/2 和一个中心点的情况统一了
right = i + 1 + (len2-2)/2; //可以写成i + len2/2
curr = len2 > curr.length() ? s.substring(left, right+1) : curr;
}
}
return curr;
}
public int centerExpand(String s, int i, int j){
// 相邻两个字符不同, 直接返回0
if(s.charAt(i) != s.charAt(j))
return 0;
int len = j - i + 1;
int step=1;
while((i-step)>=0 && (j+step)<s.length()){
if(s.charAt(i-step) == s.charAt(j+step)){
//长度一次加2!
len += 2;
step++;
}
else
break;
}
return len;
}
}
题解中心扩展法
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;
}