[LeetCode] 5. Longest Palindromic Substring(最长回文子串)
-
Difficulty: Medium
-
Related Topics: String, Dynamic Programming
-
Link: https://leetcode.com/problems/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
-
How can we reuse a previously computed palindrome to compute a larger palindrome?
如何利用已确定的回文子串确定更大的回文子串? -
If "aba" is a palindrome, is "xabax" and palindrome? Similarly is "xabay" a palindrome?
如果 "aba" 是回文的,那么 "xabax" 是回文的吗?类似地,"xabay" 呢? -
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++
}
}
}