滑动窗口算法最简单的教程

1 举个栗子

给定一个字符串,找到包含的不同字符个数不超过K的最长的连续子串

同样先来几个例子,以便准确地理解问题。

例子1

输入: String="araaci", K=2 

输出: 4 

解释: 包含不同字符个数不超过2的最长的连续子串是 "araa"

例子2

输入: String="araaci", K=1 

输出: 2 

解释: 包含不同字符个数不超过1的最长的连续子串是 "aa"

例子3

输入: String="cbbebi", K=3 

输出: 5 

解释: 包含不同字符个数不超过3的最长的连续子串是 "cbbeb" 或者 "bbebi"

2 思路分析

因为字符串本质就是一个字符数组,所以本题实际上也是求子数组。只不过挑选的标准和前道题不同而已。

  • 前道题是求最短,本题是求最长。

  • 前道题是子数组元素的和不小于K,本道题是子数组元素(即字符)的种类数不超过K。明白了这两点不同,就清楚如何修改前道题的代码,来完成这道题的求解。

首先,还是从数组开头累积出一个最长的符合条件的子字符串,作为我们的窗口。其次,当滑动窗口后,尽可能避免收缩窗口,除非新窗口中包含的字符种类超过了限制。最后,我们需要实时记录窗口中包含的不同字符的个数,以便判断窗口是否符合K个不同字符的要求,这里,我们可以选择HashMap。

3 代码实现

def longest_substring_with_k_distinct(str1, k):
  window_start = 0
  max_length = 0
  char_frequency = {}

  # 在下面的循环中,我们将尽可能扩展 [window_start, window_end]
  的范围
  for window_end in range(len(str1)):
    right_char = str1[window_end]
    if right_char not in char_frequency:
      char_frequency[right_char] = 0
    char_frequency[right_char] += 1

    # 收缩滑动窗口, 直到char_frequency中的key的数目不超过K
    while len(char_frequency) > k:
      left_char = str1[window_start]
      char_frequency[left_char] -= 1
      if char_frequency[left_char] == 0:
        del char_frequency[left_char]
      window_start += 1  # 收缩窗口
    # 记录下当前最长的窗口长度
    max_length = max(max_length, window_end-window_start + 1)
  return max_length

4 思考总结

如果大家看了前道题和本题,应该会有个感觉,滑动窗口算法,其实就是将原来的暴力算法进行了优化而已。主要的优化点其实有两个:

  • 不去计算已经计算过的

  • 不去计算根据条件确定不用计算的

第一点,就是滑动前后窗口重叠部分的数字的和,第二点,就是本题和前道题中窗口每滑动一步,直接从窗口左端进行收缩,实际上就是已经确定,窗口左端再往左的部分是不用考虑的。

如果大家有机器学习的知识,滑动窗口算法很像是对暴力算法的一个剪枝算法。

再深一步讲,所谓的动态规划,其实也是对暴力算法的一个优化,也就是不去计算已经算过的或者不用计算的部分。

大家在学习一个新的算法技巧时,可以采用这个学习的思路,即先用暴力算法进行实现,然后思考新的算法技巧是在哪部分进行了优化,这样就更容易理解算法技巧的本质。

如果理解了本文,那么leetcode上这道 https://leetcode.com/problems/fruit-into-baskets/  medium的题,对你就不在话下了。

推荐阅读:

一文读懂高并发情况下的常见缓存问题

用 Django 开发基于以太坊智能合约的 DApp

一文读懂 Python 分布式任务队列 celery

5 分钟解读 Python 中的链式调用

用 Python 创建一个比特币价格预警应用

▼点击成为社区会员   喜欢就点个在看吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值