5. 最长回文子串(Longest Palindromic Substring)M

英文题目

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

  • Example 1:

  • Input: s = "babad"
    Output: "bab"
    Note: "aba" is also a valid answer.
    
  • Example 2:

  • Input: s = "cbbd"
    Output: "bb"
    
  • Example 3:

  • Input: s = "a"
    Output: "a"
    
  • Example 4:

  • Input: s = "ac"
    Output: "a"
    
  • Constraints:

  • 1 <= s.length <= 1000

  • s consist of only digits and English letters (lower-case and/or upper-case),

中文题目

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

  • 示例 1:

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

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

暴力解法

  • 遍历所有的子串,判断是否是回文

  • 时间复杂度O(n^3),空间复杂度O(1)

    # 超时
    class Solution:
        def longestPalindrome(self, s: str) -> str:
            max_len, res = 0, ""
            for start in range(len(s)):
                # 注意python的字符是左闭右开
                for end in range(start+1, len(s)+1):
                    if self.is_palindrome(s[start:end]) and end - start > max_len:
                        res = s[start:end]
                        max_len = end - start
            return res
        
        def is_palindrome(self, s: str):
            start, end = 0, len(s) - 1
            while start < end:
                if s[start] != s[end]:
                    return False
                start, end = start + 1, end - 1
            return True
    

动态规划

  • 令dp[i][j]表示S[i]到S[j]所表示的子串是否是回文子串,是为True,不是为False

  • 有两种情况:

  • 1.若S[i]==S[j],那么只要S[i+1]到S[j-1]是回文子串,S[i]到S[j]就是回文子串

  • 2.若S[i]!=S[j],那么S[i]到S[j]一定不是回文子串

  • 状态转移方程:

  • 边界:dp[i][i]=1, dp[i][i+1]=(S[i]==S[i+1])?True:False(边界即为长度为1和2的子串)

  • 枚举方式应该是按照子串的长度和子串的初始位置进行枚举

  • 时间复杂度为O(n^2),空间复杂度O(n^2)

    # python3: 时间 4248 ms, 击败 32.90%; 内存 23.8 MB, 击败 25.44%
    class Solution:
        def longestPalindrome(self, s: str) -> str:
            n = len(s)
            if n < 2:
                 return s
            
            maxLen = 1
            begin = 0
            # dp[i][j] 表示 s[i..j] 是否是回文串
            dp = [[False] * n for _ in range(n)]
            for i in range(n):
                dp[i][i] = True
    
            # 先枚举子串长度
            for length in range(2, n+1):
                # 枚举左边界,左边界的上限设置可以宽松
                for left in range(n):
                    # 由 length 和 left 可以确定右边界,
                    # 即 right - left + 1 = length 得
                    right = left + length - 1
                    # 如果右边界越界,就可退出当前循环
                    if right >= n:
                        break
    
                    if s[left] != s[right]:
                        dp[left][right] = False
                    else:
                        # 此处判断是为了字符串长度为 2 
                        if right - left == 1:
                            dp[left][right] = True
                        else:
                            dp[left][right] = dp[left+1][right-1]
    
                    # 只要 dp[i][L] == true 成立,就表示子串 s[i..L]
                    # 是回文,此时记录回文长度和起始位置
                    if dp[left][right] and length > maxLen:
                        maxLen = length
                        begin = left
            return s[begin:begin+maxLen]
    
    // c++: 时间 396 ms, 击败 22.92%; 内存 22.2 MB, 击败 46.69%
    class Solution {
    public:
        string longestPalindrome(string s) {
            int n = s.size();
            if (n < 2) {
                return s;
            }
    
            vector<vector<bool>> dp(n, vector<bool>(n, false));
            int maxLen = 1;
            int begin = 0;
            for (int i = 0; i < n; ++i) {
                dp[i][i] = true;
            }
    
            for (int length = 2; length <= n; ++length) {
                for (int left = 0; left < n; ++left) {
                    int right = left + length - 1;
                    if (right >= n) {
                        break;
                    }
    
                    if (s[left] != s[right]) {
                        dp[left][right] = false;
                    } else {
                        if (right - left == 1) {
                            dp[left][right] = true;
                        } else {
                            dp[left][right] = dp[left+1][right-1];
                        }
                    }
    
                    if (dp[left][right] && length > maxLen) {
                        maxLen = length;
                        begin = left;
                    }
                }
            }
            return s.substr(begin, maxLen);
        }
    };
    
    // java: 时间 171 ms, 击败 19.48%; 内存 45 MB, 击败 10.27%
    class Solution {
        public String longestPalindrome(String s) {
            int n = s.length();
            if (n < 2) {
                return s;
            }
            
            int maxLen = 1;
            int begin = 0;
            boolean[][] dp = new boolean[n][n];
            for (int i = 0; i < n; ++i) {
                dp[i][i]= true;
            }
    
            for (int length = 2; length <= n; ++length) {
                for (int left = 0; left < n; ++left) {
                    int right = left + length - 1;
                    if (right >= n) {
                        break;
                    }
    
                    if (s.charAt(left) != s.charAt(right)) {
                        dp[left][right] = false;
                    } else {
                        if (right - left == 1) {
                            dp[left][right] = true;
                        } else {
                            dp[left][right] = dp[left+1][right-1];
                        }
                    }
    
                    if (dp[left][right] && length > maxLen) {
                        maxLen = length;
                        begin = left;
                    }
                }
            }
            return s.substring(begin, begin+maxLen);
        }
    }
    
    // go: 时间 68 ms, 击败 29%; 内存 7.2 MB, 击败 33.75%
    func longestPalindrome(s string) string {
        n := len(s)
        if n < 2 {
            return s
        }
    
        maxLen := 1
        begin := 0
        dp := make([][]bool, n)
        for i := 0; i < n; i++ {
            dp[i] = make([]bool, n)
            dp[i][i] = true
        }
    
        for length := 2; length <= n; length++ {
            for left := 0; left < n; left++ {
                right := left + length - 1
                if right >= n {
                    break
                }
    
                if s[left] != s[right] {
                    dp[left][right] = false
                } else {
                    if right - left == 1 {
                        dp[left][right] = true
                    } else {
                        dp[left][right] = dp[left+1][right-1]
                    }
                }
    
                if dp[left][right] && length > maxLen {
                    maxLen = length
                    begin = left
                }
            }
        }
        return s[begin:begin+maxLen]
    }
    
    // javascript: 时间 824 ms, 击败 14.76%; 内存 69 MB, 击败 21.47%
    /**
     * @param {string} s
     * @return {string}
     */
    var longestPalindrome = function(s) {
        let n = s.length;
        if (n < 2) {
            return s;
        }
    
        let maxLen = 1;
        let begin = 0;
        const dp = new Array(n);
        for (let i = 0; i < n; ++i) {
            dp[i] = new Array(n).fill(false);
            dp[i][i] = true;
        }
    
        for (let length = 2; length <= n; ++length) {
            for (let left = 0; left < n; ++left) {
                let right = left + length - 1;
                if (right >= n) {
                    break;
                }
    
                if (s.charAt(left) != s.charAt(right)) {
                    dp[left][right] = false;
                } else {
                    if (right - left == 1) {
                        dp[left][right] = true;
                    } else {
                        dp[left][right] = dp[left+1][right-1];
                    }
                }
                if (dp[left][right] && length > maxLen) {
                    maxLen = length;
                    begin = left;
                }
            }
        }
        return s.substring(begin, begin+maxLen);
    };
    

中心扩散

  • 枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为次「回文中心」下的最长回文串长度

  • 时间复杂度O(n^2),空间复杂度O(1)

    # python3: 时间 544 ms, 击败 64.14%; 内存 16 MB, 击败 77.84%
    class Solution:
        def longestPalindrome(self, s: str) -> str:
            res = ""
            for i in range(len(s)):
                res1 = self.expandAroundCenter(s, i, i)
                res2 = self.expandAroundCenter(s, i, i+1)
                res = res1 if len(res) < len(res1) else res
                res = res2 if len(res) < len(res2) else res
            return res
        
        def expandAroundCenter(self, s: str, l: int, r: int) -> str:
            while 0 <= l and r < len(s) and s[l] == s[r]:
                l, r = l-1, r+1
            return s[l+1:r]
    
    // c++: 时间 52 ms, 击败 65.79%; 内存 164.9 MB, 击败 27.51%
    class Solution {
    public:
        string longestPalindrome(string s) {
            string res = "";
            for (int i = 0; i < s.size(); ++i) {
                string res1 = expandAroundCenter(s, i, i);
                string res2 = expandAroundCenter(s, i, i+1);
                res = res.size() < res1.size() ? res1 : res;
                res = res.size() < res2.size() ? res2 : res;
            }
            return res;
        }
    
        string expandAroundCenter(string s, int l, int r) {
            while (0 <= l && r < s.size() && s[l] == s[r]) {
                --l;
                ++r;
            }
            return s.substr(l+1, r-l-1);
        }
    };
    
    // java: 时间 20 ms, 击败 72.66%; 内存 43.3 MB, 击败 44.71%
    class Solution {
        public String longestPalindrome(String s) {
            String res = "";
            for (int i = 0; i < s.length(); ++i) {
                String res1 = expandAroundCenter(s, i, i);
                String res2 = expandAroundCenter(s, i, i+1);
                res = res.length() < res1.length() ? res1 : res;
                res = res.length() < res2.length() ? res2 : res; 
            }
            return res;
        }
    
        private String expandAroundCenter(String s, int l, int r) {
            while (0 <= l && r < s.length() && s.charAt(l) == s.charAt(r)) {
                --l; 
                ++r;
            }
            return s.substring(l+1, r);
        }
    }
    
    // go: 时间 0 ms, 击败 100%; 内存 2.3 MB, 击败 99.74%
    func longestPalindrome(s string) string {
        res := ""
        for i := 0; i < len(s); i++ {
            res1 := expandAroundCenter(s, i, i)
            res2 := expandAroundCenter(s, i, i+1)
            if len(res1) > len(res) {
                res = res1
            }
            if len(res2) > len(res) {
                res = res2
            }
        }
        return res
    }
    
    func expandAroundCenter(s string, l, r int) string {
        for 0 <= l && r < len(s) && s[l] == s[r] {
            l--;
            r++;
        }
        return s[l+1:r]
    }
    
    // javascript: 时间 72 ms, 击败 94.73%; 内存 43.8 MB, 击败 58.37%
    /**
     * @param {string} s
     * @return {string}
     */
    var longestPalindrome = function(s) {
        let res = "";
        for (let i = 0; i < s.length; ++i) {
            let res1 = expandAroundCenter(s, i, i);
            let res2 = expandAroundCenter(s, i, i+1);
            res = res.length < res1.length ? res1 : res;
            res = res.length < res2.length ? res2 : res;
        }
        return res;
    };
    
    let expandAroundCenter = function(s, l, r) {
        while (0 <= l && r < s.length && s.charAt(l) == s.charAt(r)) {
            --l;
            ++r;
        }
        return s.substring(l+1, r);
    }
    
Last Updated:
Contributors: Shiqi Lu