leetcode 3 - 无重复字符的最长子串
题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 \* 104s 由英文字母、数字、符号和空格组成
分析思路
- 首先,题目要求是最长子串,符合滑动窗口算法求解特征;
- 题目求最长子串长度,实际就是求符合条件的窗口最大长度,所以是一个可变窗口,即属于滑动窗口中的动态窗口类型;
- 对于动态窗口类型,可以通过哈希集来进行唯一性检查,在哈希集中存在,即符合条件的可以保留在窗口中,否则剔除键;
- 在给定数组 s 长度内滑动窗口:
- 计数方式:
- 扩展右边界:在第一层循环中更新,如果右边界当前的字符键不在哈希集中则新建,键初始值为 1,如果键存在则更新字符计数,如果右边界当前字符计数超过 1,说明存在重复字符,需要收缩左边界;
- 收缩左边界:收缩可以通过一个内循环来实现,每次收缩时,减少移出位置的字符计数,如果减少计数后该字符计数归 0,则剔除该字符对应键,直到右边界当前字符计数不大于 1;
- 索引方式:
- 扩展右边界:通过 for 对 s 进行遍历,如果右边界当前字符在哈希集中不存在,则新建字符键,初始值为字符当前索引,如果字符已存在,说明存在重复字符,则收缩左边界;
- 收缩左边界:获取重复字符旧索引下一位,并和当前左边界索引进行比较,如果重复字符旧索引大于左边界索引(说明重复字符在当前窗口内),则将旧索引下一位作赋值给左边界,同时更新哈希集中的重复字符索引为新索引(即右边界字符当前索引);
- 计数方式:
- 每次移动后,进行当前窗口长度和最大窗口长度比较,如果当前窗口长度大于最大窗口长度,则将当前窗口长度值赋值给最大窗口长度,然后继续计算右边界;
- 返回最大窗口长度最终结果。
go 实现示例
- 解法 1
func lengthOfLongestSubstring(s string) int {
// 获取字符串s长度
n := len(s)
// 边界情况处理:如果
if n == 0 {
return 0
}
// 定义哈希字符集,用来进行字符唯一性检查
char := make(map[byte]int)
// 定义滑动窗口的左右边界,初始值均为0,表示从头开始滑动
left, right := 0, 0
// 定义最长无重复子串长度,初始值为0
maxLength := 0
// 定义每次移动窗口时的子串长度,初始值为0
currentLength := 0
// 开始滑动窗口
for right < n {
// 如果右边界新增元素在检查字符集char中存在,表示存在重复字符,则字符计数+1,不存在则新增字符,初始计数为1
if _, exist := char[s[right]]; exist {
// 增加重复字符计数
char[s[right]]++
// 如果字符计数大于1,说明存在重复字符,减少左边界字符计数
for char[s[right]] > 1 {
char[s[left]]--
// 如果左边界字符计数归0,则从字符集中剔除
if char[s[left]] == 0 {
delete(char, s[left])
}
// 收缩左边界
left++
}
} else {
char[s[right]] = 1
}
// 每次移动,都更新当前子串长度,并与最大子串长度进行比较,如果大于最大子串长度,则更新
currentLength = right - left + 1
if currentLength > maxLength {
maxLength = currentLength
}
// 移动右边界
right++
}
// 返回最长子串长度
return maxLength
}
- 解法 2
func lengthOfLongestSubstring(s string) int {
// 获取字符串s长度
n := len(s)
// 如果字符串长度为0,则直接返回0
if n == 0 {
return 0
}
// 定义哈希集,用于检查字符唯一性
char := make(map[byte]int)
// 定义子串最长长度,初始值为0
maxLength := 0
// 定义滑动窗口左边界,初始值为0,表示从索引0开始
left := 0
// 开始进行窗口滑动,对右边界新增字符进行检查
for right := 0; right < n; right++ {
// 如果检查字符在char存在,表示当前字符为重复字符,则收缩左边界,如果上一个重复字符在当前窗口内,即将左边界位置更新为上一个重复字符的下一位
if lastIndex, exist := char[s[right]]; exist {
if lastIndex >= left {
left = lastIndex + 1
}
}
// 更新窗口字符集已有字符最新位置
char[s[right]] = right
// 如果当前窗口长度大于最大窗口长度,则更新最大窗口长度
if right-left+1 > maxLength {
maxLength = right - left + 1
}
}
// 返回最终计算结果
return maxLength
}

浙公网安备 33010602011771号