LeetCode 热题 HOT 100 3. 无重复字符的最长子串

598 阅读2分钟

题目

题解

队列


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    const len = s.length
    const queue = []
    const strArr = [...s]
    let longest = 0
    for (let i = 0; i < len; i++) {
        let existIdx = queue.indexOf(strArr[i])
        if (existIdx > -1) {
            let start = 0
            while (start <= existIdx) {
                queue.shift()
                start++
            }
        }
        queue.push(strArr[i])
        longest = Math.max(queue.length, longest)
    }
    return longest
};

滑动窗口

滑动窗口是字符串或者数组中常见的一种抽象概念,窗口指的是在数组或者字符串中由开始索引、结束索引所组成的一系列元素的集合([l, r) 左闭右开)。而滑动指的是窗口的边界l 、r可以左右移动。比如如果窗口往右移动1位,则变成[l+1, r+1)。


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let len = s.length
    // 左边界索引
    let l = 0
    // 右边界索引
    let r = 0
    let max = 0
    const subStr = [...s]
    const set = new Set()
    while (l < len && r < len) {
        if (!set.has(subStr[r])) {
            set.add(subStr[r++])
            max = Math.max(max, r - l)
        } else {
            set.delete(subStr[l++])
        }
    }
    return max
};

这个case最坏情况时间复杂度是O(2n),每个字符都恰好被l,r访问一次。

进阶版滑动窗口

不管是之前的队列版本、还是上述的滑动窗口版本,有个地方可以优化,时间复杂度最坏情况下是O(2n),每个字符恰好被l、r访问一次。只所以出现这个问题是因为之前如果一个元素s[k]出现在窗口[l, r)之间,我们会不断从Set删除前面元素,并通过l++来进行缓慢的左边界移动导致元素被重复访问。那么其实这步是可以加速的。

优化版就是解决上述问题,也就是说,如果s[k]在窗口[l, r)之间出现重复元素k`,那么我们直接跳过[l, k ],把l变成k+1。


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    const len = s.length
    const map = new Map()
    const strArr = [...s]
    let max = 0
    for (let l = 0, r = 0; r < len; r++) {
        if (map.has(strArr[r])) {
            l = Math.max(l, map.get(strArr[r]))
        }
        // r + 1 是为了方便重复时候直接跳到下一个位置
        map.set(strArr[r], r + 1)
        max = Math.max(max, r - l + 1)
    }
    return max
};

ps: 欢迎关注我的公众号 xyz编程日记,觉得不错的帮忙点个👍,点个在看。