Leecode3——无重复字符的最长子串 (滑动窗口,set)

2024.3.26

Leecode3——无重复字符的最长子串

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


个人解答

先上最简单的思路,序列其实就是起点和终点。用快慢指针,慢指针遍历整个字符串,作为无重复字符串的起点,快指针在慢指针右侧搜索,没见过的字符就加入max_length_list,遇到有重复的就要开始计算长度了,最后修改最大长度的值即可。

遇到个小坑,要考虑快指针把字符串遍历完的情况。代码如下:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        length = len(s)
        if length == 1:
            return 1
        max_length = 0
        for i in range(0, length):
            max_length_list = []
            for j in range(i, length):
                if s[j] not in max_length_list:
                    max_length_list.append(s[j])
                    if j == length-1:
                        max_length = max(max_length, len(max_length_list))
                else:
                    max_length = max(max_length, len(max_length_list))
                    break
        return max_length

不过两个循环,耗时还是很慢的:

耗时:1371ms(5.02%)

内存:16.4MB(88.21%)


优化,启动!

1.滑动窗口

优化上面这个代码,最简单的思路就是使用滑动窗口,类似于一个队列(先进先出)。

那么大致逻辑上就只需要一次循环:

构建当前的无重复的字串序列current_list,遍历s中的字符,如果没有出现过,就加入;如果出现过,那么就一直删去current_list的首元素,直到删到当前字符没有出现过,继续循环。

代码如下:

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        length = len(s)
        max_length = 0
        current_list = []
        for i in s:
            if i not in current_list:
                current_list.append(i)
            else:
                while(i in current_list):
                    current_list.remove(current_list[0]) # 队首元素出队
                current_list.append(i)
            max_length = max(max_length, len(current_list))
        return max_length

耗时:83ms (19.82%)

内存:16.44MB (80.04%)


2.Python中的集合set和列表list

(1)set和list在形式上的区别,set中不允许出现重复的元素,而list可以;

(2)最关键的一点:查找效率不同!!!

单次查询,list的时间复杂度为O(n),需要遍历整个list;而由于set进行了去重,使用了红黑树这种高效的查询技巧,时间复杂度为O(logn)(理想情况下为O(1))。

所以,在涉及大规模查找时,效率:set>dict>list(list中,[]>list())

(3)set是无序的,和list不同,因此set不能用来索引和切片。

一个用set写的代码参考如下:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0
        left = 0
        lookup = set()
        n = len(s)
        max_len = 0
        cur_len = 0
        for i in range(n):
            cur_len += 1
            while s[i] in lookup:
                lookup.remove(s[left])
                left += 1
                cur_len -= 1
            if cur_len > max_len:
                max_len = cur_len
            lookup.add(s[i])
        return max_len

tips:

1.set常用的操作:增加元素(add(x)),删除元素(remove(x)), x in set,len()等。

2.由于无法使用切片,所以上面这段代码里面,使用了left来记录要删除的起点位置,lookup里删除的不是lookup里重复的地方,而是s中left对应的元素。这一点还是很巧妙的!

耗时:51ms (88.89%)

内存:16.54MB (41.24%)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值