[路飞]_每天刷leetcode_77(最长回文子串 Longest palindromic substring)

这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战

最长回文子串 Longest palindromic substring

LeetCode传送门5. 最长回文子串

题目

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

Given a string s, return the longest palindromic substring in s.

Example:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

输入:s = "cbbd"
输出:"bb"
复制代码

Constraints:

  • 1 <= s.length <= 1000
  • s consist of only digits and English letters.

思考线


解题思路

根据回文中心解题

由于回文字都有一个回文中心,这个回文中心要么是一个字符,要么是2个字符。

我们可以循环s,然后把s[i] 或者s[i],s[i+1]当做回文中心来处理。 我们很容易得到以下代码。

function longestPalindrome(s: string): string {
    let longest = '';
    const len = s.length
    for (let i = 0; i < len; i++) {
        // 回文中心为i ,或者i,i+1

        let j = i
        let item = s[i]
        let m = 1;
        while (j - m >= 0 && j + m < len) {
            const b = s[j - m]
            const a = s[j + m]
            if (b === a) {

                item = b + item + a;
                m++
            } else {
                break;
            }
        }
        if (longest.length < item.length) longest = item;

        // 为i, i + 1;
        if (s[i] === s[i + 1] && i + 1 < len) {
            let j = i
            let item = s[i] + s[i + 1];
            let m = 1;
            while (j - m >= 0 && j + m + 1 < len) {
                const b = s[j - m]
                const a = s[j + m + 1]
                if (b === a) {

                    item = b + item + a;
                    m++
                } else {
                    break;
                }
            }
            if (longest.length < item.length) longest = item;

        }



    }
    return longest;

};
复制代码

写到这里,我已经暂时想不出如何做到更优解了。于是我去看看别的小伙伴写的答案。思路是一样的思路,不过有个小伙伴的代码封装的比我要好,贴下代码:

function longestPalindrome(s: string): string {
    if(s.length < 2){
        return s
    }
    let start:number = 0
    let maxLength:number = 1
    function fn(left:number,right:number){
        while(left >= 0 && right < s.length && s[left] === s[right]){
            if(right - left + 1 > maxLength){
                maxLength = right -left + 1
                start = left
            }
            left--
            right++
        }
    }
    for(let i:number = 0;i<s.length;i++){
        fn(i - 1,i + 1)
        fn(i,i + 1)
    }
    return s.substring(start,start + maxLength)
};
复制代码

时间复杂度

O ( n 2 ) O(n^2) : n是字符串长度。 长度为1 和2 的回文中心分别有nn-1个,每个回文中心最多向外扩展 O(n)次。

动态规划思想解题

动归问题,真的是一看就会,一写就废,还是没能写出来。看了题解,记下来心得体会,希望下次能做出来。

关于动态规划我们要思考一下几点

  1. 找到"状态"和"选择"

  2. 明确dp数组/函数的定义

  3. 寻找“状态”之间的关系

  4. 在这道题中我们的状态是什么呢? 我们求的是最小最长回文子串,在子串中,存在两种状态,是回文子串为true,不是回文子串为false.

  5. 明确dp数组/ 函数的定义。 很明显确定一个字串需要至少两个indexleft, right, 所以我们把dp定义为一个二维数组,P[left, right], 其中left、right是子串的首尾点。

  6. 寻找"状态"之间的关系 假设一个子串为s[left, right](left < right)

    • 若其本身不是回文子串,那么它不能再构成更长的回文子串。
    • 若其本身是回文子串,那么如果还满足s[left -1] ===s[right+1]s[left-1, right +1]也是回文字符串。

    那么我们可以得到动态规划的状态转移方程

    P ( i , j ) = P ( i + 1 , j 1 ) & & s [ i ] = = = s [ j ] P(i, j) = P(i+1, j -1) \&\& s[i] === s[j]

    上面的转移方程为子串长度大于2, 我们还要考虑动态规划中的边界条件,即子串的长度为1或者2的情况。

    • 如果子串长度为1很明显它是一个回文串
    • 如果长度为2,只要其字母相同,它也是一个回文串

    由此我们可以写出动态规划的边界条件:

    { P ( i , i ) = t r u e P ( i , i + 1 ) = s [ i ] = = = s [ i + 1 ] {}\begin{cases} P(i,i) = true\\ P(i, i+1) = s[i] === s[i+1] \end{cases}

有了以上的思路,我们就可以完成动态规划了。最终的答案即为所有P(i,j) = truej-i+1(即子串长度)的最大值。

根据以上思路,我写的代码如下:

function longestPalindrome(s: string): string {
    const len = s.length;
    let maxLen = 1;
    let start = 0;

    const dp: boolean[][] = Array.from(new Array(len), () => new Array(len).fill(false));

    // 边界条件, 长度为1的都为回文串
    for (let i = 0; i < len; i++) {
        dp[i][i] = true;
    }
    for (let L = 2; L <= len; L++) {
        for (let i = 0; i < len; i++) {
            // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
            const j = L + i - 1;
            // 如果右边界越界,就可以退出当前循环
            if (j >= len) {
                break;
            }

            if (s[i] != s[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;
                start = i;
            }
        }
    }
    return s.substring(start, start + maxLen);

};
复制代码

时间复杂度

O ( n 2 ) O(n^2) : 动态规划的状态总数为 n 2 n^2

这就是我对本题的解法,如果有疑问或者更好的解答方式,欢迎留言互动。

おすすめ

転載: juejin.im/post/7068200390457557022
おすすめ