持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,[点击查看活动详情](juejin.cn/post/714765…
题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入: s = "babad"
输出: "bab"
解释: "aba" 同样是符合题意的答案。
示例 2:
输入: s = "cbbd"
输出: "bb"
提示:
1 <= s.length <= 1000s仅由数字和英文字母组成
解题思路
- 子串就是截取字符串中的连续的一部分,我们可以暴力枚举每一个子串。通过两重for循环即可。对于回文串的验证,最简单的想法是可以从字符串两边一组组向里对比是否相同,如果没有不相同的说明是中心对称的,也就是是回文串。对于刚才的算法,遍历子串复杂度n^2,回文串复杂度n,整体时间复杂度为
O(n^3)不满足题目的数据范围。 - 下面我们换一种思路,每一个回文串有一个对称中心,以中心向外,两两相同。这样枚举中心复杂度为n,整体复杂度将为
O(n^2)。这个方法可以称为中心扩散法。
func longestPalindrome(s string) string {
l, r := 0, 0
for i := 0; i < len(s); i++ {
t := [][]int{{i - 1, i + 1}, {i, i + 1}}
for _, xy := range t {
x, y := xy[0], xy[1]
for y < len(s) && x >= 0 && s[x] == s[y] {
y++
x--
}
if y-x-2 > r-l {
l, r = x+1, y-1
}
}
}
return s[l : r+1]
}
- 效果不错,但是有一个专业的算法马拉车,复杂度更低,回头再做介绍。
func longestPalindrome(s string) string {
start, end := 0, -1
t := "#"
for i := 0; i < len(s); i++ {
t += string(s[i]) + "#"
}
t += "#"
s = t
arm_len := []int{}
right, j := -1, -1
for i := 0; i < len(s); i++ {
var cur_arm_len int
if right >= i {
i_sym := j * 2 - i
min_arm_len := min(arm_len[i_sym], right-i)
cur_arm_len = expand(s, i-min_arm_len, i+min_arm_len)
} else {
cur_arm_len = expand(s, i, i)
}
arm_len = append(arm_len, cur_arm_len)
if i + cur_arm_len > right {
j = i
right = i + cur_arm_len
}
if cur_arm_len * 2 + 1 > end - start {
start = i - cur_arm_len
end = i + cur_arm_len
}
}
ans := ""
for i := start; i <= end; i++ {
if s[i] != '#' {
ans += string(s[i])
}
}
return ans
}
func expand(s string, left, right int) int {
for ; left >= 0 && right < len(s) && s[left] == s[right]; left, right = left-1, right+1 { }
return (right - left - 2) / 2
}
- 由于数据量太小,时间反而不尽人意,空间页消耗更多