算法系列-第十八题

77 阅读2分钟

题目名称:最长回文子串

给你一个字符串 s,找到 s 中最长的回文

子串

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

输入:s = "babad"

输出:"bab"

解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"

输出:"bb"


/**

* 解法一:动态规划

* 思路:https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/

* 时间复杂度:O(n^2),其中 n 是字符串的长度。动态规划的状态总数为 O(n^2),对于每个状态,我们需要转移的时间为 O(1)。

* 空间复杂度:O(n^2),即存储动态规划状态需要的空间。

*/

function longestPalindrome(s: string): string {

const len = s.length;

if (len < 2) return s;

  


let maxLen = 1;

let begin = 0;

// dp[i][j] 表示 s[i..j] 是否是回文串

const dp: boolean[][] = [];

for (let i = 0; i < len; i++) {

dp[i] = [];

}

// 初始化:所有长度为 1 的子串都是回文串

for (let i = 0; i < len; i++) {

dp[i][i] = true;

}

const charArray = s.split("");

// 递推开始

// 先枚举子串长度

for (let L = 2; L <= len; L++) {

// 枚举左边界,左边界的上限设置可以宽松一些

for (let i = 0; i < len; i++) {

// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得

let j = L + i - 1;

// 如果右边界越界,就可以退出当前循环

if (j >= len) break;

  


if (charArray[i] != charArray[j]) {

dp[i][j] = false;

} else {

if (j - i < 3) {

dp[i][j] = true;

} else {

dp[i][j] = dp[i + 1][j - 1];

}

}

  


// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置

if (dp[i][j] && j - i + 1 > maxLen) {

maxLen = j - i + 1;

begin = i;

}

}

}

  


return s.substring(begin, begin + maxLen);

}


/**

* 解法二:中心扩展算法

* 思路:如果是回文子串,那么它的左右两边的元素肯定是对称的,我们可以使用左右指针,从当前元素向两边扩散,以找到最长字串。

* 时间复杂度:O(n^2)。

* 空间复杂度:O(1)。

*/

function longestPalindrome(s: string): string {

let res = "";

for (let i = 0; i < s.length; i++) {

const str1 = palindrome(s, i, i);

const str2 = palindrome(s, i, i + 1);

res = res.length > str1.length ? res : str1;

res = res.length > str2.length ? res : str2;

}

return res;

}

function palindrome(s: string, left: number, right: number): string {

// 左右指针,从s[l]和s[r]向两边扩散,找到最长回文串

while (left >= 0 && right < s.length && s[left] === s[right]) {

left--;

right++;

}

return s.substr(left + 1, right - left - 1);

}