LeetCode 5. 最长回文子串

5. 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:

输入: "cbbd"
输出: "bb"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

  最常用的方法应该有三个,暴力解,动态规划和中心扩展,

  动态规划

  当字符串长度为1的时候,必然是个回文串,那长度为3的情况下呢,就需要s[i] == s[i + 3]的情况下才能形成回文

  或者说长度为2时,需要s[i] == s[i + 1]形成回文

  so,可以得出个结论,子串从 i 到 j 如果想是个回文,需满足两个条件:

  1. 子串[i + 1, j - 1]是个回文

  2.字符s[i] == s[j]

  SO, 可以得出状态方程

  dp[i, j] = dp[i + 1, j - 1] && s[i] == s[j]

  用个dp[s.len][s.len]来表示子串从i到j是否是回文串,未必最长回文串;  

  在dp总,dp[i][i]比如是个回文,因为长度只有1(i - i + 1);

  所以可以将dp[i][i]直接先赋值为true,并且作为最初始的判断条件

        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }

  进而考虑边界条件dp[i + 1][j - 1]的边界问题:

  如果从s[i + 1, j - 1]的长度小于2,既为0 或者1的时候:

  如果是0,那必然是回文; 如果是1,则只需要判断s[i] s[j]是否相等即可,可以把这两个判定归类在s[i]==s[j]的情况下,dp[i][j]为回文

  代码如下:

string longestPalindrome(string s) {
        // 特判
        int len = s.length();
        if (len < 2) {
            return s;
        }

        int maxLen = 1;
        int begin = 0;

        // dp[i][j] 表示 s[i, j] 是否是回文串
        vector<vector<int>> dp(len, vector<int>(len));

        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        for (int j = 1; j < len; j++) {
            for (int i = 0; i < j; i++) {
                printf("str = %s 
", s.substr(i, j - i + 1).c_str());

                if (s[i] != s[j]) {
                    dp[i][j] = false;
                }
                else {
                    if (j - i < 3) { //(j-1) - (i+1) + 1 < 2 从i到j的内部小于等于1
                        dp[i][j] = true;
                    }
                    else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substr(begin, begin + maxLen);
}

  中心扩展

   核心思想是对每个字符以及两个字符中间作为中心点,向两边扩展,判定是否是回文,得出最长

  为什么是每个字符以及两个字符中间作为中心点呢,因为字符长度可以是基数或者偶数

class Solution {
public:
    pair<int, int> expandAroundCenter(const string& s, int left, int right) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            --left;
            ++right;
        }
        return {left + 1, right - 1};
    }

    string longestPalindrome(string s) {
        int start = 0, end = 0;
        for (int i = 0; i < s.size(); ++i) {
            auto [left1, right1] = expandAroundCenter(s, i, i);
            auto [left2, right2] = expandAroundCenter(s, i, i + 1);
            if (right1 - left1 > end - start) {
                start = left1;
                end = right1;
            }
            if (right2 - left2 > end - start) {
                start = left2;
                end = right2;
            }
        }
        return s.substr(start, end - start + 1);
    }
};

  另:马拉车算法

  太复杂了,等我看明白先!!!

  

原文地址:https://www.cnblogs.com/gongkiro/p/13637223.html