引领网站手机网站建设的整体流程
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 * 104
s 由英文字母、数字、符号和空格组成
思路:滑动窗口 + 哈希表
- 左指针 i 初始不动,右指针 j 向右移动,
- 当 j 指向的字符在窗口内出现重复时,需要将左指针 i 移动到上一次该字符出现位置的下一位,同时,为了保证窗口内始终无重复字符,i 不能回退。因此实际更新时要
取 (当前左边界,上一次该重复字符出现的位置+1) 的较大值
。
不能回退的原因是,如果左指针回到窗口左边界左侧的旧位置,会把已经丢弃的字符重新包括进窗口,从而导致重复字符再次出现,破坏无重复子串的性质。
- 判断字符是否重复以及获取上一次出现的位置可以通过哈希表实现:key 是字符,value 是该字符上一次出现的下标。当 j 指向的字符在 map 中出现且位置在当前窗口内,就说明遇到重复字符。
- 每次窗口更新后,计算长度 j - i + 1 并维护最大值。
- 最后更新哈希表,记录当前字符最新出现的位置。
数据结构map:
m := make(map[byte]int)
map:Go 的哈希表(字典),存储键值对。
键类型 key = byte:byte 本质是 uint8,可以存储 0~255 的整数。
对应 ASCII 字符:英文大小写字母 ‘a’ ~ ‘z’、‘A’ ~ ‘Z’ ,数字 ‘0’ ~ ‘9’ ,标点符号和一些控制字符
值类型 value = int:用来记录字符在字符串中上一次出现的下标。
代码实现(Go):
详细注解:
//package main
//
//import "fmt"func lengthOfLongestSubstring(s string) int {// map: 记录每个字符上一次出现的下标// key: byte (ASCII字符,0~255,可以直接存字母、数字和符号)// value: int,字符上一次出现的索引m := make(map[byte]int)ans := 0 // 保存最长无重复子串长度// i: 窗口左边界, j: 窗口右边界for i, j := 0, 0; j < len(s); j++ {// 检查 s[j] 是否在窗口内出现过if v, ok := m[s[j]]; ok {// 更新左边界 i:// 如果 v+1 > i,说明上一次出现的重复字符仍在窗口里// 所以把 i 移动到 v+1(重复字符下一位)// 如果 v+1 <= i,说明重复字符在窗口外,不需要移动 iif v+1 > i {i = v + 1}}// 更新最大长度if j-i+1 > ans {ans = j - i + 1}// 记录 s[j] 的最新出现位置m[s[j]] = j}return ans
}//func main() {
// s := "abcabcbb"
// fmt.Println(lengthOfLongestSubstring(s)) // 输出3
//}
无注释:
//package main
//
//import "fmt"func lengthOfLongestSubstring(s string) int {m := make(map[byte]int)ans, i := 0, 0for j := 0; j < len(s); j++ {if v, ok := m[s[j]]; ok {if v+1 > i {i = v + 1}}if j-i+1 > ans {ans = j - i + 1}m[s[j]] = j}return ans
}//func main() {
// s := "abcabcbb"
// fmt.Println(lengthOfLongestSubstring(s)) // 输出3
//}