LeetCode 3.去重复字符的最长子串【中等】

53 阅读2分钟

题干

给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列, 不是子串。

题解

遇到类似这种求满足xx条件的最长子串的问题很容易就能想到要使用滑动窗口,设置leftright为滑动窗口的两个端点,如果在遍历到right位置时,如果s[right]s[left:right]的大范围内没出现过,则将right右移;如果发现s[right]s[left:right]的范围内出现过,且上一次出现的位置为ileft <= i < right),那么我们可以把left指针直接指向i+1,将i+1作为新的子串起点,这是因为我们知道s[i] = s[right],如果新的left处在老的lefti之间,那么子串一定是不满足没有重复字符的条件的,只有新的left >= i+1才可能出现新的解。为了在出现重复字符时确认left的新位置,我们设置一个chIdxMap用来记录某个字符上一次出现的下标。根据上面的思路,可以写出下面的代码:

func lengthOfLongestSubstring(s string) int {
	// 滑动窗口的左右指针
	left, right := 0, 0
	n := len(s)
	ans := 0
	// 使用一个map存放字符到下标的映射
	chIdxMap := make(map[byte]int)
	for right < n {
		curCh := s[right]
		// 判断当前字符是否之前出现过,如果出现过,就意味着在当前滑动窗口下找到了最长的不重复子串,计算结果
		// 随后将滑动窗口的左指针挪动到上一次出现该字符的下一个位置
		// 注意这里要判断这个字符上一次出现的位置在不在滑动窗口里面,如果不在的话要左指针就不移动,如果移动了的话可能把左指针回溯了
		if idx, ok := chIdxMap[curCh]; ok {
			ans = max(ans, right-left)
			left = max(idx+1, left)
		}
		// 更新map,并使滑动窗口的右指针向右侧移动
		chIdxMap[curCh] = right
		right++
	}
	// 因为之前只有在出现重复字符时才会计算解,为了处理遍历到最后都没出现重复字符的情况,最后还要在ans和right-left之间取max
	return max(ans, right-left)
}