题目
给你一个字符串 s,找到 s 中最长的回文子串。 示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
暴力法:一目了然
function longestPalindrome(s) {
int len = s.length;
if (len < 2) {
return s;
}
int maxLen = 1;
let res = s.substring(0, 1);
// 枚举所有长度大于等于 2 的子串
for (let i = 0; i < len - 1; i++) {
for (let j = i + 1; j < len; j++) {
if (j - i + 1 > maxLen && valid(s, i, j)) {
maxLen = j - i + 1;
res = s.substring(i, j + 1);
}
}
}
return res;
}
function valid(s, left, right) {
// 验证子串 s[left, right] 是否为回文串
while (left < right) {
if (s[left] != s[right]) {
return false;
}
left++;
right--;
}
return true;
}
}
方法一:动态规划
- 如果一个字符串是回文串,那么在它左右分别加上一个相同的字符,那么它一定还是一个回文串
- 如果在一个不是回文字符串的字符串两端添加任何字符,或者在回文串左右分别加不同的字符,得到的一定不是回文串 使用dp[i][j] 表示 s 中从 i 到 j(包括 i 和 j)是否可以形成回文,将上面的描述转化为代码:
if (s[i] === s[j] && dp[i + 1][j - 1]) {
dp[i][j] = true;
}
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function (s) {
let n = s.length;
let dp = Array.from(new Array(n), () => new Array(n).fill(0)); // 初始化二维数组用于记录dp[i][j]是否为true
let res = "";
for (let i = n - 1; i >= 0; i--) { // 从字符串的末尾开始构建dp[i][j]
for (let j = i; j < n; j++) {
dp[i][j] = s[i] == s[j] && (j - i < 2 || dp[i + 1][j - 1]);
if (dp[i][j] && j - i + 1 > res.length) {
res = s.substring(i, j + 1);
}
}
}
return res;
}
方法二: 中心扩张
如图:
function longestPalindrome1(s) {
if (s == null || s.length == 0) {
return "";
}
let strLen = s.length();
let left = 0;
let right = 0;
let len = 1;
let maxStart = 0;
let maxLen = 0;
for (let i = 0; i < strLen; i++) {
left = i - 1;
right = i + 1;
while (left >= 0 && s[left] == s[i]) {
len++;
left--;
}
while (right < strLen && s[]right] == s[i]) {
len++;
right++;
}
while (left >= 0 && right < strLen && s[right] == s[left]) {
len = len + 2;
left--;
right++;
}
if (len > maxLen) {
maxLen = len;
maxStart = left;
}
len = 1;
}
return s.substring(maxStart + 1, maxStart + maxLen + 1);
}