首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >算法题:最长回文子串

算法题:最长回文子串

作者头像
编码如写诗
发布2026-03-02 20:47:12
发布2026-03-02 20:47:12
690
举报
文章被收录于专栏:编码如写诗编码如写诗

算法题:最长回文子串

今天刷到道挺有意思的题,最长回文子串,LeetCode第5题。

题目是这样的:给你一个字符串,找到其中最长的回文子串。

回文就是正读反读都一样的字符串,比如"aba"、"abba"。

题目理解

举个栗子,字符串是"babad"。

最长回文子串是"bab"或"aba",长度都是3。

如果是"cbbd",最长回文子串是"bb",长度是2。

简单来说,就是在一个字符串里找最长的对称子串。

思路分析

最暴力的解法就是枚举所有子串,然后判断是不是回文。

这个思路听着简单,但时间复杂度是O(n³),太慢了。

稍微聪明点的方法是中心扩散。

以每个字符为中心(或两个字符中间为中心),向两边扩散。

如果是回文就继续扩,不是就停。

记录最长的那个。

这个方法的复杂度是O(n²)。

还有一种方法是用动态规划。

定义dp[i][j]表示s[i:j+1]是不是回文。

状态转移:如果s[i]==s[j],且dp[i+1][j-1]是回文,那么dp[i][j]就是回文。

这个方法复杂度也是O(n²),但空间复杂度是O(n²),比中心扩散费空间。

复杂度分析

中心扩散法:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)

动态规划法:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(n²)

代码实现

代码语言:javascript
复制
package main

import (
"fmt"
)

// 中心扩散法
func longestPalindrome(s string) string {
iflen(s) < 2 {
return s
 }

 start, end := 0, 0

for i := 0; i < len(s); i++ {
// 以单个字符为中心
  len1 := expandAroundCenter(s, i, i)
// 以两个字符中间为中心
  len2 := expandAroundCenter(s, i, i+1)

  maxLen := len1
if len2 > maxLen {
   maxLen = len2
  }

if maxLen > end-start {
   start = i - (maxLen-1)/2
   end = i + maxLen/2
  }
 }

return s[start : end+1]
}

func expandAroundCenter(s string, left, right int) int {
for left >= 0 && right < len(s) && s[left] == s[right] {
  left--
  right++
 }
return right - left - 1
}

// 动态规划法
func longestPalindromeDP(s string) string {
iflen(s) < 2 {
return s
 }

 n := len(s)
 dp := make([][]bool, n)
for i := range dp {
  dp[i] = make([]bool, n)
 }

 start, maxLen := 0, 1

// 单个字符都是回文
for i := 0; i < n; i++ {
  dp[i][i] = true
 }

// 检查长度为2的子串
for i := 0; i < n-1; i++ {
if s[i] == s[i+1] {
   dp[i][i+1] = true
   start = i
   maxLen = 2
  }
 }

// 检查长度大于2的子串
for length := 3; length <= n; length++ {
for i := 0; i <= n-length; i++ {
   j := i + length - 1
   if s[i] == s[j] && dp[i+1][j-1] {
    dp[i][j] = true
    start = i
    maxLen = length
   }
  }
 }

return s[start : start+maxLen]
}

func main() {
// 测试用例
 s1 := "babad"
 fmt.Printf("输入: %s, 最长回文: %s\n", s1, longestPalindrome(s1))

 s2 := "cbbd"
 fmt.Printf("输入: %s, 最长回文: %s\n", s2, longestPalindrome(s2))

 s3 := "a"
 fmt.Printf("输入: %s, 最长回文: %s\n", s3, longestPalindrome(s3))

 s4 := "ac"
 fmt.Printf("输入: %s, 最长回文: %s\n", s4, longestPalindrome(s4))

// 测试动态规划法
 fmt.Println("\n使用动态规划法:")
 fmt.Printf("输入: %s, 最长回文: %s\n", s1, longestPalindromeDP(s1))
 fmt.Printf("输入: %s, 最长回文: %s\n", s2, longestPalindromeDP(s2))
}

注意事项

有几点容易踩坑:

第一,中心扩散要考虑两种情况:以单个字符为中心和以两个字符中间为中心。

第二,动态规划的初始化要注意,单个字符都是回文,长度为2的子串要单独判断。

第三,动态规划的状态转移是dp[i][j] = (s[i]==s[j]) && dp[i+1][j-1],注意边界条件。

第四,字符串长度为0或1的时候要特殊处理,直接返回原字符串。

这道题还有个更高级的解法,叫Manacher算法,时间复杂度是O(n),不过面试一般不会要求掌握。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-02-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编码如写诗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目理解
  • 思路分析
  • 复杂度分析
  • 代码实现
  • 注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档