【Day45】647. 回文子串 5. 最长回文子串

文章目录

647.回文子串

题目:

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串

子字符串 是字符串中的由连续字符组成的一个序列

示例 1
输入 :s = "abc"
输出 :3
解释:三个回文子串: "a", "b", "c"

示例 2
输入 :s = "aaa"
输出 :6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

提示:

1 <= s.length <= 1000

s 由小写英文字母组成


思路:

题目要找:字符串里所有连续的、正读反读一样的子串数量。

回文串一定有中心点,只有两种情况:

  1. 奇数长度 :中心点是 1 个字符(如 aaba
  2. 偶数长度 :中心点是 2 个相邻字符(如 aaabba

中心扩展法:

  1. 遍历字符串的每一个位置,把它当作奇数回文中心
  2. 遍历每一对相邻位置,把它们当作偶数回文中心
  3. 从中心往左右两边扩散,只要左右字符相等,就是一个回文
  4. 每找到一个回文,总数 +1

代码实现(Go):

go 复制代码
package main

import "fmt"

// expand 函数作用:
// 从给定的左指针 l、右指针 r 开始,向左右两边扩展
// 统计以 l、r 为中心的回文子串个数
func expand(s string, l, r int) int {
	// 记录本次扩展能找到的回文数量
	count := 0
	// 获取字符串长度
	n := len(s)

	// 循环条件:
	// 1. 左指针不越界(l >= 0)
	// 2. 右指针不越界(r < n)
	// 3. 左右两边字符相等(s[l] == s[r])
	// 满足 → 说明是回文
	for l >= 0 && r < n && s[l] == s[r] {
		count++       // 找到一个回文,计数 +1
		l--           // 左指针继续向左扩展
		r++           // 右指针继续向右扩展
	}

	// 返回本次中心扩展找到的回文总数
	return count
}

// countSubstrings 作用:统计字符串中所有回文子串的数量
func countSubstrings(s string) int {
	// 最终结果:所有回文子串的总数
	res := 0
	n := len(s)

	// 遍历字符串的每一个位置,作为回文的中心
	for i := 0; i < n; i++ {
		// 情况 1:奇数长度回文,中心是 1 个字符 i
		res += expand(s, i, i)

		// 情况 2:偶数长度回文,中心是 2 个字符 i 和 i+1
		res += expand(s, i, i+1)
	}

	// 返回最终统计的总数量
	return res
}

func main() {
	// 示例 1:输入 abc → 输出 3(a、b、c)
	fmt.Println(countSubstrings("abc"))

	// 示例 2:输入 aaa → 输出 6
	fmt.Println(countSubstrings("aaa"))
}
  • 时间复杂度O(n²)

    • 外层遍历所有中心:O(n)
    • 每个中心最多向左右扩展 n 次:O(n)
    • 总复杂度:n * n = n²
  • 空间复杂度O(1)

    • 只使用了固定几个变量(res、left、right、n
    • 没有开辟额外的数组/切片,和输入长度无关

5.最长回文子串

题目:

给你一个字符串 s,找到 s 中最长的 回文 子串

示例 1
输入 :s = "babad"
输出 :"bab"
解释:"aba" 同样是符合题意的答案。

示例 2
输入 :s = "cbbd"
输出:"bb"

提示:

1 <= s.length <= 1000

s 仅由数字和英文字母组成


思路:

所有回文串,都一定有一个中心点。

回文只有两种:

  1. 奇数长度 :中心点是 1 个字符
    如:aabaabcba
  2. 偶数长度 :中心点是 2 个相邻字符
    如:aaabbaacc a

  1. 遍历字符串的每一个位置
  2. 对每个位置,做两种尝试
    • 把它当作 奇数长度回文中心,向左右扩展
    • 把它和下一个字符当作 偶数长度回文中心,向左右扩展
  3. 每次扩展时,只要左右字符相等,就是回文
  4. 全程记录最长回文的起始位置和长度
  5. 最后根据记录,截取出最长回文子串返回。

代码实现(Go):

go 复制代码
package main

import "fmt"

// expand 从 l、r 中心向两边扩展,返回【最长回文的长度】
func expand(s string, l, r int) int {
	n := len(s)
	// 不越界且字符相等,就继续扩展
	for l >= 0 && r < n && s[l] == s[r] {
		l--
		r++
	}
	// 循环结束时,最后一次有效回文长度 = r - l - 1
	return r - l - 1
}

// longestPalindrome 寻找最长回文子串
func longestPalindrome(s string) string {
	n := len(s)
	if n == 0 {
		return ""
	}

	// 记录最长回文的起始位置和长度
	// 最短回文长度是1(单个字符)
	start := 0
	maxLength := 1

	// 遍历每个可能的回文中心
	for i := 0; i < n; i++ {
		// 奇数长度回文
		len1 := expand(s, i, i)
		// 偶数长度回文
		len2 := expand(s, i, i+1)

		// 取两种情况中更长的那个
		maxLen := max(len1, len2)

		// 如果当前长度 > 记录的最长长度,则更新
		if maxLen > maxLength {
			maxLength = maxLen
			start = i - (maxLen-1)/2
		}
	}

	// 截取并返回最长回文
	return s[start : start+maxLength]
}

func main() {
	fmt.Println(longestPalindrome("babad")) // 输出 bab / aba
	fmt.Println(longestPalindrome("cbbd"))  // 输出 bb
}
  • 时间复杂度O(n²)
    遍历每个中心 O(n),每个中心最多扩展 O(n)
  • 空间复杂度O (1)
    只使用了几个变量,没有开辟额外空间

相关推荐
Trouvaille ~2 小时前
【优选算法篇】哈希表——空间换时间的极致艺术
c++·算法·leetcode·青少年编程·蓝桥杯·哈希算法·散列表
我是咸鱼不闲呀3 小时前
力扣Hot100系列22(Java)——[图论]总结(岛屿数量,腐烂的橘子,课程表,实现Trie(前缀树))
java·leetcode·图论
AMoon丶3 小时前
Golang--内存管理
开发语言·后端·算法·缓存·golang·os
阿Y加油吧4 小时前
力扣打卡——搜索二维矩阵、相交链表
线性代数·leetcode·矩阵
普贤莲花4 小时前
【2026年第11周---写于20260322】
程序人生·算法·leetcode
小白自救计划4 小时前
力扣知识点杂集
算法·leetcode·哈希算法
承渊政道4 小时前
【优选算法】(实战体验滑动窗口的奇妙之旅)
c语言·c++·笔记·学习·算法·leetcode·visual studio
lars_lhuan5 小时前
Go Context
golang
AMoon丶5 小时前
Golang--垃圾回收
java·linux·开发语言·jvm·后端·算法·golang