【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)
    只使用了几个变量,没有开辟额外空间

相关推荐
6Hzlia1 天前
【Hot 100 刷题计划】 LeetCode 48. 旋转图像 | C++ 矩阵变换题解
c++·leetcode·矩阵
Morwit1 天前
【力扣hot100】 1. 两数之和
数据结构·c++·算法·leetcode·职场和发展
py有趣1 天前
力扣热门100题之岛屿的数量(DFS/BFS经典题)
leetcode·深度优先·宽度优先
qinian_ztc1 天前
frida 14.2.18 安装报错解决
算法·leetcode·职场和发展
Wenweno0o1 天前
Eino - 错误处理与稳定性
golang·智能体·eino
田梓燊1 天前
2026/4/11 leetcode 3741
数据结构·算法·leetcode
王码码20351 天前
Go语言中的Elasticsearch操作:olivere实战
后端·golang·go·接口
Tomhex1 天前
Go语言import用法详解
golang·go
小肝一下1 天前
每日两道力扣,day8
c++·算法·leetcode·哈希算法·hot100
Tomhex1 天前
Golang空白导入的真正用途
golang·go