无重复字符的最长子串,经典滑动窗口问题

52 阅读1分钟

版本一,暴力解法,虽然超时,其中有些步骤值得学习

    // 暴力解法:遍历数组的所有的区间,然后找到最长没有重复字符的区间
    // 时间复杂度:O(n^3)
    // 空间复杂度:O(n)
    
    func lengthOfLongestSubstring1(s string) int {
    
        var n = len(s)
        if n <= 1 {
            return n
        }
    
        var allUnique func(start, end int) bool
        allUnique = func(start, end int) bool { //判断区间[start, end]有无重复元素
            var set = make(map[byte]bool)//这里先声明,直接读会返回nil
            for i := start; i <= end; i++ {
                if set[s[i]] {
                    return false
                }
                set[s[i]] = true
            }
            return true
        }  

        var maxLen = 1
        for i := range s {
            for j := i + 1; j < n; j++ {
                if allUnique(s, i, j) && j - i + 1 > maxLen {//这里还有个优化呢
                    maxLen = j - i + 1
                }
            }
        }
        return maxLen
}

暴力解法存在重复计算,同一个子串会进行多次判断 【是否存在重复字符】
我们可以做如下的优化:

如果 s[i, j) 子串没有重复字符的话,那么如果要判断 s[i, j] 没有重复字符的话,我们只需要判断s[i, j) 中是否存在 s[j] 即可

    // 优化后的滑动窗口
    // 时间复杂度:O(n)
    // 空间复杂度:O(n)
func lengthOfLongestSubstring3(s string) int {
    var n = len(s)
    if n <= 1 {
        return n
    }
    
    maxLen := 1
    left, right, window := 0, 0, make(map[byte]int)
    
    for right < n {
    
        rightChar := s[right]
        rightCharIndex := 0
        
        if _, ok := window[rightChar]; ok { //判断s[i, j)中是否有s[j]
            rightCharIndex = window[rightChar] //如果窗口有了,返回一个索引
        }
        
        if rightCharIndex > left { //更新left
            left = rightCharIndex
        }
        if right - left + 1 > maxLen { //更新maxLen
            maxLen = right - left + 1
        }
        
        window[rightChar] = right + 1 //map中key是每个byte,value
        是当前窗口后一位
        right++
    } 
    return maxLen
}

精简代码,思路清晰,最终版本

func lengthOfLongestSubstring(s string) int {
    var n = len(s)
    
    //边界处理
    if n <= 1 {
        return n
    }
    
    //初始化
    var maxLen = 1
    var left, right, window = 0, 0, [128]int{} 
    //这里得[]int{},位置==字符ASCII,内容存right+1
    
    for right < n {
    
        var rightChar = s[right]
        var rightCharIndex = window[rightChar]
        
        if rightCharIndex > left { //这种情况只发生在第二次相遇
            left = rightCharIndex
        }
        
        maxLen =max(right-left+1, maxlen) 
        window[rightChar] = right + 1 //存得是当前维护得窗口下一位
        right++
    }
    return maxLen
}