python代码练习:滑动窗口

前置:

  • 什么是滑动窗口
  • -inf 与 inf 用法
  • 理解滑动窗口主要弄清以下问题:
    • 1、窗口内的数据代表着什么?
    • 2、什么情况下需要扩展窗口右边界?
    • 3、什么情况下需要收缩窗口左边界?
    • 4、什么时候计算窗口大小?

题目1:长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

from typing import List
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
            sum=0
            i=0
            temp = float('inf')
            for j in range(len(nums)):  # 遍历数组种每一个item,并累加
               sum+=nums[j]
               
               while sum>=s:    # 满足目标条件后,开始缩小窗口
                   L = j-i+1    
                   temp = min(temp,L)   
                   # 目标是计算sum>=s时的最小窗口程度,该目标与while条件一直,所以写在while里面
                   # 并在窗口左边界变化之前就计算窗口大小
                   sum-=nums[i]
                   i+=1
            if temp==float('inf') :return  0
            else:return temp

s=Solution()
print(s.minSubArrayLen(s=7,nums=[2,3,1,2,4,3]))
print(s.minSubArrayLen(s=4,nums=[1,4,4]))
print(s.minSubArrayLen(s=11,nums=[1,1,1,1]))

题目2:水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

from collections import defaultdict
from typing import List
class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        a,i=len(fruits),0
        tree=0
        temp=0
        dic_tree = defaultdict(int)
        for j in range(a):  # 从左到右遍历每一个item,对每个item计数并将数据放入字典
            dic_tree[fruits[j]]+=1
            if dic_tree[fruits[j]] == 1:
                tree+=1 # 树的数量+到1时,意味着有新类型的树增加

            while tree>2:   # 树的类型超过2种时,开始缩小窗口
                dic_tree[fruits[i]]-=1
                if dic_tree[fruits[i]]==0:
                    tree-=1
                i+=1
            temp=max(temp,j-i+1)
            # 目标是计算tree<=2时窗口的大小,该目标与上文while条件不一致,所以写在while之外
        return temp

s=Solution()
print(s.totalFruit(fruits=[0,1,2,1]))
print(s.totalFruit(fruits=[1,2,1]))

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

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

例1:s='ababaa'  最长子串是 ab ,长度为2

例2:s='cccccc'  最长子串是 c ,长度为1

例1:s='dvdf'  最长子串是 vdf ,长度为3

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        temp_s=set()
        j=-1
        temp=0
        # 遍历窗口左边界
        for i in range(len(s)):
            #窗口右边界 in set,就会跳出while循环,从for开始继续遍历左边界(收缩窗口左边界)
            if i!=0:
                temp_s.remove(s[i-1])
            # 遍历窗口右边界
            # 窗口右边界一旦 not in set,就继续扩展右边界
            while j + 1 < len(s) and s[j + 1] not in temp_s:
                temp_s.add(s[j+1])
                j+=1
            temp = max(temp,j-i+1)
        return temp

s=Solution()
print(s.lengthOfLongestSubstring(s='dvdf'))

题目4:找到字符串中所有字母异位词

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

# 以p_len长度的数据作为窗口
# 窗口内字符计数 == p串的字符计数 时,满足题目要求,返回窗口左边界
class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        s_len,p_len  = len(s),len(p)
        s_count=[0]*26
        p_count=[0]*26

        for i  in range(p_len):    # p_len长度时,初始窗口字符计数、p串字符计数
            s_count[ord(s[i])-97]+=1
            p_count[ord(p[i])-97]+=1

        if s_count == p_count:
            res.append(0)

        for i in range(s_len - p_len):    # 窗口右移
            s_count[ord(s[i])-97]-=1    # 左边界变化,旧字符计数-1
            s_count[ord(s[i+p_len])-97]+=1    # 右边界变化,新字符计数+1
            if s_count==p_count:
                res.append(i+1)    

        return res

题目5:串联所有单词的子串( 滑动窗口结合哈希表 )

给定一个字符串 s 和一个字符串数组 words words 中所有字符串 长度相同

 s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。

  • 例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd""cdabef", "cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

# 窗口内数据:words元素的组合
# 窗口总长度:words元素个数m * 单个元素长度n = total_l
##### words单词计数 == 窗口单词计数 时,满足题目要求
# 如何从s获取窗口:对s切片,即s[i:i+total_l]
# 如何从窗口中切分出单词:通过步长确定单词长度(0,total_l,n),对窗口切片w[j:j+n]

from typing import List
from collections import Counter

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        res = []
        m, n, ls = len(words), len(words[0]), len(s)
        total_l = m*n   # 窗口总长度
        cnt = Counter(words)    # 对words单词计数
        for i in range(ls-n+1): 
            w = s[i:i+total_l]  # 切片,切出一个窗口
            tmp = []
            for j in range(0,total_l,n):    # 按步长(单词长度)切分窗口,获取窗口内的单词
                tmp.append(w[j:j+n])
            if Counter(tmp) == cnt: # 窗口单词计数 == wordes单词计数时,返回窗口左边界i
                res.append(i)
        return res
if __name__ == '__main__':
    s=Solution()
    print(s.findSubstring(s='barfoothefoobarman',words=["foo","bar"]))

其他:

Count() 用法

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值