1. 滑动窗口简介

滑动窗口协议(Sliding Window Protocol),该协议是 TCP协议 的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认。因此该协议可以加速数据的传输,提高网络吞吐量。

Sliding window algorithm is used to perform required operation on specific window size of given large buffer or array.
滑动窗口算法是在给定特定窗口大小的数组或字符串上执行要求的操作。
This technique shows how a nested for loop in few problems can be converted to single for loop and hence reducing the time complexity.
该技术可以将一部分问题中的嵌套循环转变为一个单循环,因此它可以减少时间复杂度。
简而言之,滑动窗口算法在一个特定大小的字符串或数组上进行操作,而不在整个字符串和数组上操作,这样就降低了问题的复杂度,从而也达到降低了循环的嵌套深度。其实这里就可以看出来滑动窗口主要应用在数组和字符串上。
滑动窗口 - 图1

2. 滑动窗口框架

滑动窗口的应用场景有几个特点:

  1. 需要输出或比较的结果在原数据结构中是连续排列的;(寻找子串)
    2. 每次窗口滑动时,只需观察窗口两端元素的变化,无论窗口多长,每次只操作两个头尾元素,当用到的窗口比较长时,可以显著减少操作次数;
    3. 窗口内元素的整体性比较强,窗口滑动可以只通过操作头尾两个位置的变化实现,但对比结果时往往要用到窗口中所有元素。

滑动窗口算法模板如下:

第一步:定义快慢指针及相关变量。
第二步:右指针移动,窗口扩大。
第三步:根据条件,更新子串信息。
第四步:左指针移动,窗口收缩。

  1. func longestOnes(nums []int, k int) int {
  2. zeros := 0 //存放条件
  3. l,r := 0,0 //快慢指针
  4. res := 0 //用于存放结果集
  5. for r < len(nums){
  6. if nums[r] == 0{
  7. zeros += 1
  8. }
  9. /*左窗口滑动条件*/
  10. for zeros > k{
  11. if nums[l] == 0{
  12. zeros -= 1
  13. }
  14. l += 1
  15. }
  16. /*左窗口滑动条件*/
  17. res = maxNum(res,r-l+1) //处理结果集
  18. r += 1
  19. }
  20. return res
  21. }

3. 固定滑窗长度题目


643. 子数组最大平均数 I 简单

给你一个由 n 个元素组成的整数数组 nums 和一个整数 k。

请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

任何误差小于 10-5 的答案都将被视为正确答案。

示例 1:
输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75

示例 2:
输入:nums = [5], k = 1
输出:5.00000

提示:
n == nums.length
1 <= k <= n <= 105
-104 <= nums[i] <= 104

代码:

  1. func findMaxAverage(nums []int, k int) float64 {
  2. sum := 0
  3. l,r := 0,0
  4. ans := -100000.0 //因为有可能是负数,所以不能初始化为0
  5. for r < len(nums){
  6. sum += nums[r]
  7. //滑窗长度为k时,更新最大平均数
  8. if r-l+1 == k{
  9. ans = max(ans,float64(sum)/float64(k))
  10. sum -= nums[l]
  11. l ++
  12. }
  13. r ++
  14. }
  15. return ans
  16. }
  17. func max(a,b float64)float64{
  18. if a > b{
  19. return a
  20. }
  21. return b
  22. }

551. 学生出勤记录 I 简单

给你一个字符串 s 表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤、迟到、到场)。记录中只含下面三种字符:

‘A’:Absent,缺勤
‘L’:Late,迟到
‘P’:Present,到场
如果学生能够 同时 满足下面两个条件,则可以获得出勤奖励:

按 总出勤 计,学生缺勤(’A’)严格 少于两天。
学生 不会 存在 连续 3 天或 连续 3 天以上的迟到(’L’)记录。
如果学生可以获得出勤奖励,返回 true ;否则,返回 false 。

示例 1:
输入:s = “PPALLP”
输出:true
解释:学生缺勤次数少于 2 次,且不存在 3 天或以上的连续迟到记录。

示例 2:
输入:s = “PPALLL”
输出:false
解释:学生最后三天连续迟到,所以不满足出勤奖励的条件。

提示:
1 <= s.length <= 1000
s[i] 为 ‘A’、’L’ 或 ‘P’

题解概要:

1.利用滑窗判断学生是否有三次以上连续迟到。
2.遍历得到学生缺勤次数。

注意:此题可不使用滑窗,代码有更简单写法,但使用滑窗beat双100%。

代码:

  1. func checkRecord(s string) bool {
  2. l,r := 0,0
  3. pNum := 0
  4. ok := true
  5. for r < len(s){
  6. if s[r] == 'A'{
  7. pNum += 1
  8. }
  9. for r-l+1 > 3{
  10. l += 1
  11. }
  12. if r-l+1 == 3&&s[l]=='L'&&s[l+1]=='L'&&s[l+2]=='L'{
  13. ok = false
  14. }
  15. r += 1
  16. }
  17. if pNum <= 1 && ok == true{
  18. return true
  19. }
  20. return false
  21. }

187. 重复的DNA序列 中等

DNA序列 由一系列核苷酸组成,缩写为 ‘A’, ‘C’, ‘G’ 和 ‘T’.。
例如,”ACGAATTCCG” 是一个 DNA序列 。
在研究 DNA 时,识别 DNA 中的重复序列非常有用。
给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。

示例 1:
输入:s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT”
输出:[“AAAAACCCCC”,”CCCCCAAAAA”]

示例 2:
输入:s = “AAAAAAAAAAAAA”
输出:[“AAAAAAAAAA”]

提示:
0 <= s.length <= 105
s[i]==’A’、’C’、’G’ or ‘T’

题解概要:

1.利用滑窗找到长度为10子串。
2.使用map记录子串出现的次数。
3.遍历map,若子串出现1次以上则加入到结果数组中。

代码:

  1. func findRepeatedDnaSequences(s string) []string {
  2. sMap := make(map[string]int)
  3. l,r := 0,0
  4. res := []string{}
  5. for r < len(s){
  6. /*左窗口滑动条件*/
  7. for r-l+1 > 10{
  8. l += 1
  9. }
  10. /*左窗口滑动条件*/
  11. /*窗口长度满足条件*/
  12. if r-l+1 == 10{
  13. sMap[s[l:r+1]] += 1
  14. }
  15. r += 1
  16. }
  17. /*左窗口滑动条件*/
  18. for k,v := range sMap{
  19. if v > 1{
  20. res = append(res,k)
  21. }
  22. }
  23. return res
  24. }

1052. 爱生气的书店老板 中等

有一个书店老板,他的书店开了 n 分钟。每分钟都有一些顾客进入这家商店。给定一个长度为 n 的整数数组 customers ,其中 customers[i] 是在第 i 分钟开始时进入商店的顾客的编号,所有这些顾客在第 i 分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。
当书店老板生气时,那一分钟的顾客就会不满意,若老板不生气则顾客是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 minutes 分钟不生气,但却只能使用一次。
请你返回 这一天营业下来,最多有多少客户能够感到满意 。

示例 1:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], minutes = 3
输出:16
解释:书店老板在最后 3 分钟保持冷静。
感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.

示例 2:
输入:customers = [1], grumpy = [0], minutes = 1
输出:1

提示:
n == customers.length == grumpy.length
1 <= minutes <= n <= 2 * 104
0 <= customers[i] <= 1000
grumpy[i] == 0 or 1

题解概要:

1.先求出原本满意客户的数量。
2.再求出x分钟内(x长度子数组),生气客户的最大数量。
3.将两者相加,便是客户的最大值。

代码:

  1. func maxSatisfied(customers []int, grumpy []int, minutes int) int {
  2. sum := 0
  3. for i,v := range grumpy{
  4. if v == 0{
  5. sum += customers[i]
  6. }
  7. }
  8. l,r := 0,0
  9. mSum := 0
  10. mMax := 0
  11. for r < len(grumpy){
  12. if grumpy[r] == 1{
  13. mMax += customers[r]
  14. }
  15. if r-l+1 == minutes{
  16. mSum = max(mSum,mMax)
  17. if grumpy[l] == 1{
  18. mMax -= customers[l]
  19. }
  20. l++
  21. }
  22. r ++
  23. }
  24. return sum + mSum
  25. }
  26. func max(a,b int)int{
  27. if a > b{
  28. return a
  29. }
  30. return b
  31. }

4.非固定滑窗长度题目


3. 无重复字符的最长子串 中等

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

题解概要:

1.使用散列表记录字符出现次数。
2.若窗口出现重复字符,则窗口左边界收缩,直至窗口内无重复字符。

注意:对移动滑窗来说,若求子串的最长,可使用如下模板。若求所有子串,无法使用此模板。

代码:

  1. func lengthOfLongestSubstring(s string) int {
  2. window := [128]byte{}
  3. ans := 0
  4. l,r := 0,0
  5. for r < len(s){
  6. //收缩窗口
  7. for window[s[r]] > 0{
  8. window[s[l]] --
  9. l++
  10. }
  11. window[s[r]] ++
  12. ans = max(ans,r-l+1)
  13. r++
  14. }
  15. return ans
  16. }
  17. func max(a int,b int)int{
  18. if a > b{
  19. return a
  20. }
  21. return b
  22. }

1004. 最大连续1的个数 III 中等

给定一个二进制数组 nums 和一个整数 k ,如果可以翻转最多k 个 0 ,则返回 数组中连续 1 的最大个数 。

示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

提示:
1 <= nums.length <= 105
nums[i] 不是 0 就是 1
0 <= k <= nums.length

题解概要:

  1. 本题可理解为求一个至多含有K个0的最长子数组长度。
  2. 当窗口内的0大于k时,收缩窗口。

代码:

  1. func longestOnes(nums []int, k int) int {
  2. ans := 0
  3. zeroNum := 0
  4. l,r := 0,0
  5. for r < len(nums){
  6. if nums[r] == 0{
  7. zeroNum ++
  8. }
  9. //窗口收缩
  10. for zeroNum > k{
  11. if nums[l] == 0{
  12. zeroNum --
  13. }
  14. l ++
  15. }
  16. ans = max(ans,r-l+1)
  17. r ++
  18. }
  19. return ans
  20. }
  21. func max(a int,b int)int{
  22. if a > b {
  23. return a
  24. }
  25. return b
  26. }

904. 水果成篮 中等

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

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

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

示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

提示:
1 <= fruits.length <= 105
0 <= fruits[i] < fruits.length

题解概要:

1.本题其实是求 至多包含两种数字连续子数组的最大长度。

代码:

  1. func totalFruit(fruits []int) int {
  2. m := make(map[int]int)
  3. ans := 0
  4. l,r := 0,0
  5. for r < len(fruits){
  6. m[fruits[r]] ++
  7. //若水果种类超过2种,则收缩窗口
  8. for len(m) > 2{
  9. m[fruits[l]] --
  10. if m[fruits[l]] == 0{
  11. delete(m,fruits[l])
  12. }
  13. l ++
  14. }
  15. ans = max(ans,r-l+1)
  16. r ++
  17. }
  18. return ans
  19. }
  20. func max(a,b int)int{
  21. if a > b{
  22. return a
  23. }
  24. return b
  25. }

209. 长度最小的子数组 中等

给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

题解概要:

1.由于数组内元素都大于0,因此本题可使用滑动窗口解决法
2.若滑窗内总和大于target,则收缩滑窗直至总和小于等于target。

代码:

  1. func minSubArrayLen(target int, nums []int) int {
  2. ans := math.MaxInt32 //由于结果求的是最小值,则需要初始化为int32的最大值。
  3. sum := 0
  4. l,r := 0,0
  5. for r < len(nums){
  6. sum += nums[r]
  7. for sum >= target{
  8. ans = min(ans,r-l+1)
  9. sum -= nums[l]
  10. l++
  11. }
  12. r ++
  13. }
  14. if ans == math.MaxInt32{
  15. return 0
  16. }
  17. return ans
  18. }
  19. func min(a,b int)int{
  20. if a < b{
  21. return a
  22. }
  23. return b
  24. }

713. 乘积小于K的子数组 中等 移动窗口

给定一个正整数数组 nums和整数 k 。
请找出该数组内乘积小于 k 的连续的子数组的个数。

示例 1:
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。

示例 2:
输入: nums = [1,2,3], k = 0
输出: 0

提示:
1 <= nums.length <= 3 * 104
1 <= nums[i] <= 1000
0 <= k <= 106

题解概要:

总体来说可以套用滑动窗口模板。
此题最重要的是 18行。要理解为什么是r-l+1,以下为leetcode一位大佬的解释:

right-left+1的切入点是思维要放在区间的右边往左边延伸,例如区间[1, 2, 3, 4]满足要求,固定住right(4)的点,可选区间右[4]、[4, 3]、[4, 3, 2]、[4, 3, 2, 1]即为数组的长度,也就是right-left+1。而right是递增的,此时[1, 2, 3]的区间已经处理完([3]、[3, 2]、[3、2、1])。如果从left为切入点,就会有[1, 2, 3, 4]和[1, 2, 3]都有[1],不就是重复了的错乱思维。

关于移动窗口求子数组个数时,应找到窗口的边界值,找到窗口下子数组关系,这样才能得到准确的子数组数目。

代码:

  1. func numSubarrayProductLessThanK(nums []int, k int) int {
  2. wPro := 1
  3. l,r := 0,0
  4. res := 0
  5. for r < len(nums){
  6. wPro *= nums[r]
  7. for wPro >= k&&l<len(nums){
  8. wPro = wPro / nums[l]
  9. l += 1
  10. }
  11. //理解这里是关键。
  12. if wPro < k{
  13. res += r-l+1
  14. }
  15. r += 1
  16. }
  17. return res
  18. }

438. 找到字符串中所有字母异位词 中等固定滑窗

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:
输入: s = “cbaebabacd”, p = “abc”
输出: [0,6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。

示例 2:
输入: s = “abab”, p = “ab”
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 “ab”, 它是 “ab” 的异位词。
起始索引等于 1 的子串是 “ba”, 它是 “ab” 的异位词。
起始索引等于 2 的子串是 “ab”, 它是 “ab” 的异位词。

提示:
1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母

题解概要:

1.使用两个散列表分别记录p串、窗口内各个字母出现的次数。
2.当窗口长度与p串一致时,再判断是否为异位词即可。

代码:

  1. func findAnagrams(s string, p string) []int {
  2. rArr := []int{}
  3. l,r := 0,0
  4. wArr := [26]int{} //记录窗口内每个字母出现的次数,判断是否为异位时使用。
  5. pArr := [26]int{} //记录p串每个字母出现的次数,判断是否为异位时使用。
  6. for _,v := range p {
  7. pArr[v-'a'] += 1
  8. }
  9. for r < len(s) {
  10. wArr[s[r]-'a'] += 1 //记录窗口内每个字母出现的次数
  11. for r-l+1>len(p){ //窗口收缩
  12. wArr[s[l]-'a'] -= 1
  13. l += 1
  14. }
  15. if r-l+1 == len(p)&&isArgthm(wArr,pArr){
  16. rArr = append(rArr,l)
  17. }
  18. r += 1
  19. }
  20. return rArr
  21. }
  22. /*当窗口中各元素出现的次数与p串各元素出现的次数一致,可认为是异位词*/
  23. func isArgthm(wArr [26]int,pArr [26]int)bool{
  24. for i,_ := range wArr{
  25. if wArr[i] != pArr[i]{
  26. return false
  27. }
  28. }
  29. return true
  30. }

567. 字符串的排列 中等

给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。

示例 1:
输入:s1 = “ab” s2 = “eidbaooo”
输出:true
解释:s2 包含 s1 的排列之一 (“ba”).

示例 2:
输入:s1= “ab” s2 = “eidboaoo”
输出:false

提示:
1 <= s1.length, s2.length <= 104
s1 和 s2 仅包含小写字母

题解概要:

1.本题思路与438题代码一致,可以参考438题。

代码:

  1. func checkInclusion(s1 string, s2 string) bool {
  2. wArr := [26]int{}
  3. pArr := [26]int{}
  4. l,r := 0,0
  5. for _,v:=range s1{
  6. pArr[v-'a'] += 1
  7. }
  8. for r < len(s2){
  9. wArr[s2[r]-'a'] += 1
  10. for r-l+1 > len(s1){
  11. wArr[s2[l]-'a'] -= 1
  12. l += 1
  13. }
  14. if r-l+1 == len(s1)&& isArgithm(wArr,pArr){
  15. return true
  16. }
  17. r += 1
  18. }
  19. return false
  20. }
  21. func isArgithm(wArr [26]int,pArr [26]int)bool{
  22. for i,_ := range wArr{
  23. if wArr[i] != pArr[i]{
  24. return false
  25. }
  26. }
  27. return true
  28. }

1838. 最高频元素的频数 中等

元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组 nums 和一个整数 k 。在一步操作中,你可以选择 nums 的一个下标,并将该下标对应元素的值增加 1 。
执行最多 k 次操作后,返回数组中最高频元素的 最大可能频数 。

示例 1:
输入:nums = [1,2,4], k = 5
输出:3
解释:对第一个元素执行 3 次递增操作,对第二个元素执 2 次递增操作,此时 nums = [4,4,4] 。
4 是数组中最高频元素,频数是 3 。

示例 2:
输入:nums = [1,4,8,13], k = 5
输出:2
解释:存在多种最优解决方案:
- 对第一个元素执行 3 次递增操作,此时 nums = [4,4,8,13] 。4 是数组中最高频元素,频数是 2 。
- 对第二个元素执行 4 次递增操作,此时 nums = [1,8,8,13] 。8 是数组中最高频元素,频数是 2 。
- 对第三个元素执行 5 次递增操作,此时 nums = [1,4,13,13] 。13 是数组中最高频元素,频数是 2 。

示例 3:
输入:nums = [3,9,6], k = 2
输出:1

提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
1 <= k <= 105

题解概要:

1.这道题先排序,后利用滑动窗口,但增加的数值大于k时收缩窗口。
滑动窗口 - 图2
滑动窗口 - 图3
代码:

  1. func maxFrequency(nums []int, k int) int {
  2. sort.Ints(nums)
  3. l,r := 0,1
  4. ans := 1
  5. a := 0
  6. for r < len(nums){
  7. a += (nums[r]-nums[r-1])*(r-l)
  8. //收缩窗口
  9. for a > k{
  10. a -= nums[r] - nums[l]
  11. l++
  12. }
  13. ans = max(ans,r-l+1)
  14. r ++
  15. }
  16. return ans
  17. }
  18. func max(a,b int)int{
  19. if a > b{
  20. return a
  21. }
  22. return b
  23. }

424. 替换后的最长重复字符 中等

给定一个字符串 s 和一个整数 k 。您可以选择字符串中的任意字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。
在执行上述操作后,返回 包含相同字母的最长子字符串的长度 。

示例 1:
输入:s = “ABAB”, k = 2
输出:4
解释:用两个’A’替换为两个’B’,反之亦然。

示例 2:
输入:s = “AABABBA”, k = 1
输出:4
解释:
将中间的一个’A’替换为’B’,字符串变为 “AABBBBA”。
子串 “BBBB” 有最长重复字母, 答案为 4。

提示:
1 <= s.length <= 105
s 由小写英文字母组成
0 <= k <= s.length

题解概要:

  1. 这道题窗口的长度是不会收缩的,只有平移或扩大。

代码:

  1. func characterReplacement(s string, k int) int {
  2. ans := 0
  3. m := [26]int{}
  4. l,r := 0,0
  5. mCount := 0
  6. for r < len(s){
  7. m[s[r]-'A'] ++ //各个字符出现的次数
  8. mCount = max(mCount,m[s[r]-'A'])//统计窗口内同一字符重复的最大次数
  9. if mCount + k < r-l+1{ //若不符合条件,则整个窗口右移一位。
  10. m[s[l]-'A'] --
  11. l ++
  12. }
  13. ans = max(ans,r-l+1)
  14. r ++
  15. }
  16. return ans
  17. }
  18. func max(a,b int)int{
  19. if a > b{
  20. return a
  21. }
  22. return b
  23. }

5.前缀和题目


930. 和相同的二元子数组 中等

给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。

示例 1:
输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]

示例 2:
输入:nums = [0,0,0,0,0], goal = 0
输出:15

提示:
1 <= nums.length <= 3 * 104
nums[i] 不是 0 就是 1
0 <= goal <= nums.length

题解概要:

1.用哈希表将之前出现过的前缀和存储起来。类似于两数之和!

代码:

  1. func numSubarraysWithSum(nums []int, goal int) int {
  2. m := map[int]int{0:1} //这里要初始化
  3. sum := 0
  4. ans := 0
  5. for _,v := range nums{
  6. sum += v //更新前缀和
  7. if count,ok := m[sum - goal];ok{
  8. ans += count
  9. }
  10. m[sum] ++
  11. }
  12. return ans
  13. }