【算法训练】3.无重复字符的最长子串

参考链接:力扣

题目:

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

示例:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

思路:

1.经典解法

遍历。从i=0出发的最长无重复字符串长度,然后i=1,i=2依次判断...

每次i位置,都需要求解最长不重复子串,有两种情况:

a. 字符串本身

b. 遍历过程中遇到了出现过的字符

代码:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        s_list = [i for i in s]
        result_rlen = 0
        for i in range(len(s_list)):
            # 从i开始的子字符串列表
            i_list = s_list[i:]
            i_result = []  # i位置开始的不重复子字符列表
            for char in i_list:
                if char in i_result:
                    break

                i_result.append(char)

            # 上述遍历完成,为i位置开始的不重复列表
            result_rlen = max(result_rlen, len(i_result))

        return result_rlen

可以看到这个方法非常慢,整个过程就是遍历拿i位置出发的不重复字符串列表,拿的过程,就是遍历过程,一旦遇到重复的就跳出

时间复杂度O(N^2)

2.进阶解法

滑动窗口法:滑动窗口法是一种算法思想,一种相对大众的解题思路,具体是谁提出的不详,类似于二分法,是属于算法训练中常用的思想。

过程:

建立一个窗口lookup,左指针left

遍历数组,不停的将字符加入窗口中,若出现重复字符,从左边开始去掉重复字符,缩小窗口,过程中记录无重复窗口的长度,遍历完成后返回最大长度

核心:在于维护了一个无重复窗口,并不是遍历完成后,窗口里就是最长无重复子串,比如一个反例

abcdaede

过程如下:

a

ab

abc

abcd

abcda->bcda

bcdae

bcdaed->aed

aede->de

过程中的最长无重复子串为bcdae,最长长度为5

代码思路:

"""
思路已经清晰:
窗口lookup    lookup=set()
左指针left    left = 0
最大长度      max_len = 0
窗口加入字符  lookup.add(s[i])
维护一个窗口,如果有重复,从左边删除字符,直到没有当前字符 while循环
while s[i] in lookup:
    lookup.remove(s[left])
    left += 1

过程中记录窗口的长度,一直维持一个最大长度,最后遍历完成返回
max_len = max(max_len, len(lookup))

"""

【算法到代码的翻译】

这一步其实没有看上去这么简单,需要考虑清楚先干啥后干啥

遍历过程中:

1.判断当前元素,删窗口重复元素

2.加当前元素

3.维护最大长度

这个过程一定不能乱,核心不在于加元素,也不在于维护最大长度,在于维护干净的无重复窗口lookup,维护由两步组成,先删后加或者先加后删,取长度一定是最后一步。

为什么不能先加后删呢?

因为先加后删,根本第一步都开始不了,所以只能先删后加。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0
        lookup = set()
        left = 0
        max_len = 0
        for i in range(len(s)):
            # 上来就判断是否在窗口中,相当于如果有重复的,会先删干净窗口
            while s[i] in lookup:
                lookup.remove(s[left])
                left += 1
            # 再加元素到窗口,此时窗口一定不重复
            lookup.add(s[i])
            # 再维护最大窗口长度max(max_len, len(lookup))
            max_len = max(max_len, len(lookup))

        return max_len

效果:

 可以发现,优化了非常多,同时说明,该种方法并不是最佳解法,最佳的解法往往都是一步一步磨出来的,所以不要奢求一步到位,该代码虽然不是最佳,但已经足以应付面试。

考察点:数组,滑动窗口法,滑动窗口法的实现

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值