题目描述
给你一个字符串 s,找到 s 中最长的回文
子串
。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
输入: s = "babad"
输出: "bab"
解释: "aba" 同样是符合题意的答案。
示例 2:
输入: s = "cbbd"
输出: "bb"
提示:
1 <= s.length <= 1000s仅由数字和英文字母组成
思路
思路 1(掐头去尾法)
动态规划:因为求得是最长回文子串,子串意味着需要是连续的,我们可以记录 s[i,j] 是否为回文子串,因此可以使用 bool 类型的 dp 数组;(以 abcba 为例,假设 i = 0,j = 4,s[i] == s[j],那此时该子串是否为最长子串是否取决于 bcb 是否为最长子串,bcb 这个子串不就是 [i+1:j-1] 这个范围吗?)
当 s[i] == s[j] 时,如果 dp[i+1][j-1] = true 又或者该子串的长度 < 3(实际上就是 2,又加上 s[i] == s[j],所以它是回文子串),那么此时可以得出 s[i,j+1] 一定是回文子串;同时判断该子串是否比已经计算出的最长回文子串还要长,如果是的话,那么记录最长回文子串的起始下标和最长长度;
思路 2(中心扩散法)
利用回文性质,从一个点出发往两边扩散,记录形成的最长回文子串时的长度和起始位置。
需要注意的是,利用该方法需要处理一个特殊情况,以 abba 为例,如果我们分别从 0、1、2、3 出发进行最长回文判断得到的结果不是 4 而是 2,这是因为 bb 这种情况永远不会被考虑到。
代码
代码 1
func longestPalindrome(s string) string {
// 求得是最长子串,必须是连续性有效,所以我们定义 bool 类型的 dp
// 表示以 start 为起点,以 end 为终点的子串是否为回文子串
// if s[i] == s[j] => dp[i+1][j-1]
var n, start, maxLen int
var dp [][]bool
n = len(s)
dp = make([][]bool, n)
for i := range dp {
dp[i] = make([]bool, n)
dp[i][i] = true
}
for i := n-2; i >= 0; i-- {
for j := i+1; j < n; j++ {
if s[i] == s[j] && (dp[i+1][j-1] || j-i+1 < 3) {
dp[i][j] = true
if j-i+1 > maxLen {
start = i
maxLen = j-i+1
}
}
}
}
return s[start:start+max(maxLen ,1)]
}
图示略,可以使用二维数组模拟一下过程。
代码 2
func longestPalindrome(s string) string {
var start, maxLen int
maxLen = 1
var helper func(i, j int)
helper = func(i, j int) {
for i >= 0 && j < len(s) && s[i] == s[j] {
if maxLen < j-i+1 {
maxLen = j-i+1
start = i
}
i--
j++
}
}
for i := 0; i < len(s); i++ {
helper(i, i)
helper(i, i+1)
}
return s[start:start+maxLen]
}