Changes: - Removed all Java code implementations - Kept only Go language solutions - Renamed "## Go 解法" to "## 解法" - Removed "### Go 代码要点" sections - Cleaned up duplicate headers and empty sections - Streamlined documentation for better readability Updated files (9): - 三数之和.md - 两数相加.md - 无重复字符的最长子串.md - 最长回文子串.md - 括号生成.md - 子集.md - 单词搜索.md - 电话号码的字母组合.md - 柱状图中最大的矩形.md All 22 LeetCode Hot 100 Medium problems now use Go exclusively. Code is cleaner, more focused, and easier to follow. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
4.8 KiB
4.8 KiB
无重复字符的最长子串 (Longest Substring Without Repeating Characters)
LeetCode 3. Medium
题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解题思路
核心思想
使用滑动窗口(Sliding Window)+ 哈希表记录字符位置。
算法流程
- 维护一个窗口 [left, right]
- 使用哈希表记录每个字符最后一次出现的位置
- 遍历字符串:
- 如果当前字符在窗口内出现,移动 left 到重复字符的下一位
- 更新哈希表和最大长度
复杂度分析
- 时间复杂度:O(n),n 为字符串长度
- 空间复杂度:O(min(m, n)),m 为字符集大小
解法
func lengthOfLongestSubstring(s string) int {
// 记录字符最后出现的位置
charIndex := make(map[rune]int)
maxLength := 0
left := 0
for right, char := range s {
// 如果字符已存在且在窗口内,移动左边界
if idx, ok := charIndex[char]; ok && idx >= left {
left = idx + 1
}
// 更新字符位置
charIndex[char] = right
// 更新最大长度
if right - left + 1 > maxLength {
maxLength = right - left + 1
}
}
return maxLength
}
图解过程
字符串: "abcabcbb"
步骤1: [a]bcabcbb
left=0, right=0, maxLength=1
步骤2: [a,b]cabcbb
left=0, right=1, maxLength=2
步骤3: [a,b,c]abcbb
left=0, right=2, maxLength=3
步骤4: a[b,c,a]bcbb (发现重复,left移动)
left=1, right=3, maxLength=3
步骤5: ab[c,a,b]cbb (发现重复,left移动)
left=2, right=4, maxLength=3
步骤6: abc[a,b,c]bb (发现重复,left移动)
left=3, right=5, maxLength=3
步骤7: abca[b,c,b]b (发现重复,left移动)
left=4, right=6, maxLength=3
步骤8: abcab[c,b,b] (发现重复,left移动)
left=5, right=7, maxLength=3
结果: maxLength = 3
进阶问题
Q1: 如何返回最长子串本身?
func longestSubstring(s string) string {
charIndex := make(map[rune]int)
maxLength := 0
left := 0
start := 0 // 记录起始位置
for right, char := range s {
if idx, ok := charIndex[char]; ok && idx >= left {
left = idx + 1
}
charIndex[char] = right
if right - left + 1 > maxLength {
maxLength = right - left + 1
start = left
}
}
return s[start : start+maxLength]
}
Q2: 如果字符集有限(如只有小写字母),如何优化?
优化:使用数组代替哈希表
func lengthOfLongestSubstring(s string) int {
charIndex := [128]int{} // ASCII 字符集
for i := range charIndex {
charIndex[i] = -1
}
maxLength := 0
left := 0
for right := 0; right < len(s); right++ {
char := s[right]
if charIndex[char] >= left {
left = charIndex[char] + 1
}
charIndex[char] = right
maxLength = max(maxLength, right-left+1)
}
return maxLength
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
P7 加分项
深度理解
- 滑动窗口:维护动态窗口,左边界根据重复字符调整
- 哈希表优化:数组 vs HashMap,时间/空间权衡
- 边界处理:重复字符在窗口外的情况
实战扩展
- 流式数据:处理超大字符串或流式输入
- 多线程:分段计算后合并
- 业务场景:日志去重、用户行为分析
变形题目
总结
这道题的核心是:
- 滑动窗口:动态调整窗口边界
- 哈希表:记录字符位置,快速判断重复
- 双指针:left 和 right 指针协同移动
易错点:
- 忘记判断重复字符是否在窗口内(
idx >= left) - 更新 left 的时机
- 数组越界(使用数组代替哈希表时)
最优解法:滑动窗口 + 哈希表,时间 O(n),空间 O(min(m, n))