LeetCode题解 3. 无重复字符的最长子串

Author:baiyucraft

BLog: baiyucraft’s Home


一、题目描述

题目地址:3. 无重复字符的最长子串

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

输入: s = “abcabcbb”

输出: 3

解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

输入: s = “bbbbb”

输出: 1

解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

输入: s = “pwwkew”

输出: 3

解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。

输入: s = “”

输出: 0

二、思路以及代码

  此题一般最无脑的做法肯定是暴力解法,即遍历每个字符串,找出以该字符开头,不包含重复字符的最长子串。所以在解题前,我们先用abcabcbb这个字符串来模拟暴力解法搜索无重复字符的最长子串的过程:

  可以发现一点,如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的,原理其实很简单,当我们枚举第i个字符开头的子串时,长度为k的子串不重复,长度为k+1的子串重复,即s的i ->i+k子串中无重复元素,那显然 i+1 ->i+k子串中也无重复元素,并且我们可以继续尝试增大该子串,直到子串中出现了重复元素。这样一来就有了滑动窗口这一概念。

1.滑动窗口

思路: 我们使用左右指针leftright来确定滑动窗口的边界。在之后的的操作中,左指针向右移动一格,右指针保证左右指针范围内的是无重复字符的最大子串我,并用max_len记录下这个子串的长度。其中用集合set类型的ooc来存储判断子串中的字符是否重复。

动画:

代码:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # occ记录子串中的字符是否重复
        occ = set()
        # n为字符串s的长度
        n = len(s)
        # right为右边界,最大长度max_len初值为0
        right, max_len = -1, 0
        # left左边界开始枚举
        for left in range(n):
            # 除了第一次,左指针向右移动一格,移除一个字符
            if left != 0:
                occ.remove(s[left - 1]) 
            # 不断地移动右指针
            while right + 1 < n and s[right + 1] not in occ:
                occ.add(s[right + 1])
                right += 1
            # 第 left 到 right 个字符是一个无重复字符子串
            max_len = max(max_len, right - left + 1)
        return max_len
2.滑动窗口-优化

思路: 在上文中,我们会发现滑动的过程中left要枚举s中的每一个数,并且right也要枚举s中的每一个数,那么有什么办法能优化窗口呢?

 我们想到用右边界right来枚举字符串中的每一个字符,用名为c_dic的字典来存储字符的同时,存储该字符离right最近的下标。

left左侧边界初值为0,每次枚举right向右移动一格时:

  • 如果该字符在c_dic中,说明是个重复字符,这时:
    • left小于等于该字符上一次出现的下标,说明该窗口中的子串重复,则将left替换为该字符上一次出现的下标的后一个位置,并且将该字符下标更新为right
    • left大于该字符上一次出现的下标,则继续以left为左边界来枚举
  • 如果该字符不在c_dic中,则存入并记录该次枚举后right - left + 1的值并与max_len比较替换

动画:

LeetCode-3-3

代码:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        c_dic = {}
        # 左边界left初值为0,max_len初值为0
        left, max_len = 0, 0
        for right, c in enumerate(s):
            # 如果c在字典中 并且 左边界left小于等于该字符上一次出现的下标(重复)
            if c in c_dic and left <= c_dic[c]:
                # 将left替换为该字符上一次出现的下标的后一个位置
                left = c_dic[c] + 1
                # 将该字符下标更新为right
                c_dic[c] = right
            else:
                # 存入字典
                c_dic[c] = right
                # 计算长度
                max_len = max(max_len, right - left + 1)
        return max_len
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值