Loading

[LeetCode] 5. Longest Palindromic Substring(最长回文子串)

Description

Given a string s, return the longest palindromic substring in s.
给定一字符串 s,返回其最长回文子串。

Examples

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),

Hints

  1. How can we reuse a previously computed palindrome to compute a larger palindrome?
    如何利用已确定的回文子串确定更大的回文子串?

  2. If "aba" is a palindrome, is "xabax" and palindrome? Similarly is "xabay" a palindrome?
    如果 "aba" 是回文的,那么 "xabax" 是回文的吗?类似地,"xabay" 呢?

  3. Complexity based hint:
    If we use brute-force and check whether for every start and end position a substring is a palindrome we have O(n^2) start - end pairs and O(n) palindromic checks. Can we reduce the time for palindromic checks to O(1) by reusing some previous computation.
    更基础的提示:
    如果使用暴力搜索,检查每一个 start 和 end 位置组成的子串是否回文,则要进行 \(O(n^2)\) 次检查,每次检查花费 \(O(n)\) 时间。能否通过利用之前的结果,将回文检查的时间复杂度降至 \(O(1)\)

Solution

根据提示的指示,我们的目标是将回文检查的时间复杂度降至 \(O(1)\),这里采取的策略是边扩展边检查。对于每个字符,使用 \(O(N)\) 时间扩展,同时完成回文检查。代码如下:

class Solution {
    private var result = ""

    fun longestPalindrome(s: String): String {
        if (s.length < 2) {
            return s
        }
        if (s.length == 2) {
            return if (s[0] == s[1]) s else s.substring(1)
        }

        for (i in s.indices) {
            updatePalindrome(s, i)
        }

        return result
    }

    private fun updatePalindrome(s: String, startIndex: Int) {
        updatePalindromeOdd(s, startIndex)
        updatePalindromeEven(s, startIndex - 1, startIndex)
        updatePalindromeEven(s, startIndex, startIndex + 1)
    }

    private fun updatePalindromeOdd(s: String, startIndex: Int) {
        if (result.isEmpty()) {
            result = s.substring(startIndex, startIndex + 1)
        }
        doUpdate(s, startIndex - 1, startIndex + 1)
    }

    private fun updatePalindromeEven(s: String, leftBound: Int, rightBound: Int) {
        doUpdate(s, leftBound, rightBound)
    }

    private fun doUpdate(s: String, leftBound: Int, rightBound: Int) {
        var left = leftBound
        var right = rightBound
        while (left in s.indices && right in s.indices) {
            if (s[left] != s[right]) {
                break
            }
            if ((right - left + 1) > result.length) {
                result = s.substring(left, right + 1)
            }
            left--
            right++
        }
    }
}

实际上,奇数长度和偶数长度的情况可以合并以简化代码,简化后的代码如下:

class Solution {
    private var result = ""

    fun longestPalindrome(s: String): String {
        if (s.length < 2) {
            return s
        }
        if (s.length == 2) {
            return if (s[0] == s[1]) s else s.substring(1)
        }

        for (i in s.indices) {
            updatePalindrome(s, i)
        }

        return result
    }

    private fun updatePalindrome(s: String, startIndex: Int) {
        // 奇数长度情况
        doUpdate(s, startIndex, startIndex)
        // 偶数长度情况
        doUpdate(s, startIndex - 1, startIndex)
        doUpdate(s, startIndex, startIndex + 1)
    }

    private fun doUpdate(s: String, leftBound: Int, rightBound: Int) {
        var left = leftBound
        var right = rightBound
        while (left in s.indices && right in s.indices) {
            if (s[left] != s[right]) {
                break
            }
            if ((right - left + 1) > result.length) {
                result = s.substring(left, right + 1)
            }
            left--
            right++
        }
    }
}
posted @ 2020-12-14 11:10  Zhongju.copy()  阅读(89)  评论(0编辑  收藏  举报