题目
给定一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入: s = "babad"
输出: "bab"
解释: "aba" 也是一个有效解
示例 2:
输入: s = "cbbd"
输出: "bb"
解法1:中心扩散法
- 思路
- 从0-length查找
- 每次找的时候分奇数偶数,如果当前1/2个字符相同,且前后字符相同则向外扩展
- 找出后进行比较,更新最大值
- 时间复杂度:O(n^2)
- 代码
function longestPalindrome(s: string): string {
// 长度范围 0 - length
const { length } = s;
let max = 0, res = '';
const find = (l, r) => {
let str = '';
// 当前1/2个元素要相同
if(s[l] !== s[r]) {
return { len: 0, l, r: l }
}
// 两侧相等则扩展
while(l > 0 && r < length - 1 && s[l - 1] === s[r + 1]){
l--;
r++;
}
return {
len: r - l + 1,
l, r
}
};
const update = ({ len, l, r }) => {
if(len > max) {
max = len;
res = s.slice(l, r + 1);
}
}
for(let i = 0; i < length; i++) {
// 奇数,找以i为中心的回文子串
update(find(i, i));
// 偶数,找以i,i+1为中心的回文子串
update(find(i, i + 1));
}
return res;
};
解法2:动态规划
我们可以使用动态规划来解决此问题。首先定义状态 dp[i][j] 表示字符串 s 中第 i 到 j 个字符组成的子串是否是回文串。如果是回文串,则 dp[i][j] 的值为 true,否则为 false。
接下来考虑状态转移方程。当 s[i] == s[j] 时,只有当 dp[i+1][j-1] 为 true 时,dp[i][j] 才可能为 true。当 s[i] != s[j] 时,dp[i][j] 必然为 false。同时还需要注意一些边界条件,即当 i >= j 或 j - i <= 2 时,dp[i][j] 必然为 true。
最后遍历二维数组 dp,并记录下最长回文子串的起始位置和长度,即可得到最长回文子串。
function longestPalindrome(s: string): string {
const len = s.length;
if (len < 2) {
return s;
}
let start = 0, maxLen = 1;
const dp: boolean[][] = Array.from({ length: len }, () => new Array(len).fill(false));
for (let i = 0; i < len; i++) {
dp[i][i] = true;
}
for (let j = 1; j < len; j++) {
for (let i = 0; i < j; i++) {
if (s[i] === s[j]) {
if (j - i <= 2) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
} else {
dp[i][j] = false;
}
if (dp[i][j] && j - i + 1 > maxLen) {
start = i;
maxLen = j - i + 1;
}
}
}
return s.substring(start, start + maxLen);
}
- 时间复杂度为 ,其中 是字符串的长度。原因是我们需要遍历二维数组 dp 中的所有元素,并计算出其值。
- 空间复杂度为 ,其中 是字符串的长度。原因是我们需要使用一个二维数组 dp 来存储子串是否为回文串的状态。
解法3:manacher方法
TODO