LeetCode - 003.无重复字符的最长子串

2022-04-07

题目

来源:003.无重复字符的最长子串

给定一个字符串 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 由英文字母、数字、符号和空格组成

分析

我们想象一下,对于一个字符串,比如示例1中的 abcabcbb,从他的起始位置开始,也就是字符 a,向右遍历。遍历至第二个位置,也就是字符 b 时,他和他前面的字符 a 构成的这个子串 ab,是不含有重复字符的。假设有一个容器,我们把这两个字符构成的子串放在这个容器里,接着继续遍历。至第三个字符 c 时,我们的这个容器里不含有和 c 重复的字符,所以把字符 c 放入这个容器,此时容器里就是由 a, b, c 这三个字符构成的子串。

继续遍历至第四个字符 a,我们发现容器里包含了字符 a,那么当前容器里的子串就是目前的最长子串。显然我们不能停在这里,也许后面的字符里会出现更长的无重复字符子串。可以看出来,如果去掉容器里重复的字符 a,剩余的字符 b, c 和当前的字符 a 构成了一个新的无重复字符的子串。

再向后遍历,第五个字符 b 已经在容器中了,跟上述的情况一样,我们可以判断当前容器里的最长子串和之前的最长子串哪一个更长。接着去除容器中重复字符和他之前的字符,并把新的字符放入容器中,构成新的子串。继续遍历。

总结一下,我们有一个容器,每遇到一个新的字符,判断容器中是否存在这个字符,如果存在,就把这个字符及其之前的字符从容器中去除,并放入这个新的字符,直至遍历结束。可以看出,这个容器的长度始终在变化,他的右侧不断放入新的字符,在遇到重复字符时,将其中的重复字符及其左侧的字符去除,同时这个容器也在不断的右移,这样的容器就是一个滑动窗口

解答

impl Solution {
    pub fn length_of_longest_substring(s: String) -> i32 {
		// 记录最长子串的长度
        let mut max = 0;
		// 上文中的容器
        let mut set = std::collections::HashSet::new();
        let s_chars: Vec<_> = s.chars().collect();

		// 容器左右两侧的索引位置
        let (mut left, mut right) = (0, 0);

        while right < s_chars.len() {
            let ch = s_chars.get(right).unwrap();
			// 判断当前字符是否在容器中
            if set.contains(ch) {
                max = std::cmp::max(max, right - left);
				// 去除容器中重复字符及其左侧的字符
                while s_chars.get(left).unwrap() != ch {
                    set.remove(s_chars.get(left).unwrap());
                    left += 1;
                }
                set.remove(s_chars.get(left).unwrap());
                left += 1;
            }
			// 将新的字符放入容器中
            set.insert(ch);
            right += 1;
        }

        std::cmp::max(max, right - left) as i32
    }
}
LeetCode

Jeff Liu

LeetCode - 004.寻找两个正序数组的中位数

二叉查找树