一、题目
给你一个字符串 s,找到 s 中最长的回文子串。
二、解题思路
2.1、暴力求解法
代码:
public String longestPalindrome(String s) {
int maxLen = 1, start = 0, len = s.length();
char[] chars = s.toCharArray();
if (s.length() < 2) {
return s;
}
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
if ((j - i + 1 > maxLen) && validPalindrome(chars, i, j)) {
maxLen = j - i + 1;
start = i;
}
}
}
return s.substring(start, start + maxLen);
}
/**
* 判断字符串是否时回文字符串
* @param chars
* @param left
* @param right
* @return
*/
private boolean validPalindrome(char[] chars, int left, int right) {
while (left <= right) {
if (chars[left] != chars[right]) {
return false;
}
left++;
right--;
}
return true;
}
时间复杂度:O(n³),这里n为字符串的长度
空间复杂度:O(1),只用到常数个临时变量
2.2、中心扩散法法
代码:
class Solution {
public String longestPalindrome(String s) {
int maxLen = 1,start = 0,len = s.length();
char[] chars = s.toCharArray();
if (s.length()<2) {
return s;
}
for (int i = 0; i < len-1; i++) {
int len1 = expandAroundCenter(chars, i, i);
int len2 = expandAroundCenter(chars, i, i+1);
int curMaxLen = Math.max(len1, len2);
if (curMaxLen > maxLen) {
maxLen = curMaxLen;
start = i-(curMaxLen-1)/2;
}
}
return s.substring(start,start+maxLen);
}
private int expandAroundCenter(char[] chars, int left, int right) {
// 当 left==right时,回文中心时一个字符,回文串的长度时奇数
// 当 right=right+1时,此时回文中心时两个字符,回文串的长度时偶数
int len = chars.length;
while (left>=0 && right<len) {
if (chars[left]!=chars[right]) {
break;// 不符合回文的情况
}
left--;
right++;
}
// 返回的长度不包含left和right所对应的字符,right-left+1-2
return right-left-1;
}
}
时间复杂度:O(n²),枚举中心位置的个数时2(n-1),每一次向两边扩散检测是否是回文;
空间复杂度:O(1),只用到常数个临时变量
图解:
2.3、动态规划求解法
1、状态:dp[ i ][ j ] 表示字串s[i…j]是否为回文子串
2、状态转移方程:dp[ i ][ j ] = (s[ i ] == s[ j ] ) and dp[i+1][j-1]
3、边界条件: (j-1) - (i+1) +1 < 2 ,整理为:j - i < 3 <==> j - i + 1 < 4,即 s[i…j] 长度为 2 或者 3 的时候,就不用检查子串是否回文了(当字串为2时,里面为0,当字串为3时,里面为1,肯定时回文串)。
4、初始化:dp[ i ][ i ] = true
5、输出:在得到一个状态的值为 true 的时候,记录起始位置和长度,填表完成以后再截取。
代码:
public String longestPalindrome(String s) {
int len = s.length(), start = 0, maxLen = 1;
char[] chars = s.toCharArray();
// boolean类型的二维数组,默认情况下都是false
boolean[][] result = new boolean[len][len];
for (int i = 0; i < len; i++) {
result[i][i] = true;
}
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (chars[i] == chars[j] && (j - i < 3 || result[i + 1][j - 1])) {
/**
* 1、i表示最左边的字符下标,j表示最右边的字符下标,首先确保chars[i] == chars[j];
* 2、chars[i..j]的长度为2或3的时候,不用再检查是否时回文
* 3、当里面的字符串也是回文串
* 条件2和条件3是或的关系,条件1是必须满足的
*/
result[i][j] = true;
}
if (result[i][j] && maxLen < j - i + 1) {
maxLen = j - i + 1;
start = i;
}
}
}
return s.substring(start, start + maxLen);
}