【leetcode】3. 无重复字符的最长子串

40 阅读2分钟

leetcode-3.png

使用滑动窗口来解决这一题
使用map来存储当前进入窗口的元素
如果下一个进来的元素在map中存在,此时就需要更新滑动窗口的起始位置
如果下一个进来的元素不存在map之中,那就继续存入map之中

错误代码

var lengthOfLongestSubstring = function (s) {
    let map = new Map()
    let res = 0
    let start = 0
    for (let i = 0; i < s.length; ++i) {
        if (map.has(s[i])) {
            start = map.get(s[i]) + 1
        }
        map.set(s[i], i)
        res = Math.max(res, i - start + 1)
    }
    return res
};

当测试用例为 'abba'
可以看到图片中的红框,此时当'a'再次进入if的时候,start = 0 + 1 = 1, i = 3
start变小了,会导致窗口变大,要防止这种情况的出现
此时会导致错误
所以当重复的字符再次出现的时候,要选择此时重复出现的字符的最大索引
image.png

正确代码

map

var lengthOfLongestSubstring = function (s) {
    let map = new Map()
    let res = 0
    let start = 0
    for (let i = 0; i < s.length; ++i) {
        if (map.has(s[i])) {
            start = Math.max(start, map.get(s[i]) + 1)
        }
        map.set(s[i], i)
        res = Math.max(res, i - start + 1)
    }
    return res
};

set

在set.has(s[right])为真的时候,为什么要删除左边?

s = "abba"

  • 初始:left=0, right=0, set={}
  • s[right]=a 不在 set → 加入 → set={a}, right=1
  • s[right]=b 不在 set → 加入 → set={a,b}, right=2
  • s[right]=b 在 set
    • 不能删 s[right],因为它还没加进去
    • 我们删 s[left] → 删 a,set={b}, left=1
    • 还是重复,因为窗口 [1,1] 里还有个 b
    • 继续删 s[left] → 删 b,set={}, left=2
    • 现在 s[right]=b 可以加进来了 → set={b}, right=3

这样才能保证窗口始终没有重复。
删除 s[left] 是为了不断缩小窗口,直到能安全地接纳 s[right]

var lengthOfLongestSubstring = function (s) {
    let left = 0,
        right = 0;
    let set = new Set();
    let maxLen = 0;
    while (right < s.length) {
        if (set.has(s[right])) {
            set.delete(s[left]);
            left++;
        } else {
            set.add(s[right]);
            right++;
            maxLen = Math.max(maxLen, right - left);
        }
    }
    return maxLen;
};