【算法笔记day two】滑动窗口(不定长版)

前言 

hello大家好,本期文章紧接着上期,讲述滑窗的下一个大分类——不定长。

定长滑窗请看我上期文章,有详细介绍。温馨提醒,代码大部分为手搓,答案方法不唯一。如果想要优雅的版本可以去找其他题解,我的代码只是会把注释标清楚。另外题目不会有很多,只是其中比较有代表性的节选。

不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,以及求子数组个数。

注:滑动窗口相当于在维护一个队列。右指针的移动可以视作入队,左指针的移动可以视作出队。

一、求最长/最大

一般题目都会有‘最多’的要求,并且题目的解题套路与写法都很像。

1、基础部分

例题1 — 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 由英文字母、数字、符号和空格组成
class Solution:def lengthOfLongestSubstring(self, s: str) -> int:g = defaultdict(int) #初始化字典用来计算字符数量res = ans = 0for i, x in enumerate(s): #通过i来计算滑窗的左右边界if g[x] <= 0: #当右侧可以纳入时g[x] += 1res += 1ans = max(ans, res)elif g[x] > 0:while res >= 0 and g[x] > 0: #不可以纳入时减到0为止g[s[i - res]] -= 1 #可以直接减不要判断,#因为滑窗内始终保持字典数字不大于1res -= 1 #要记得变化res的值,否则会死循环g[x] += 1res += 1ans = max(ans, res)return ans

#思路

用字典中字符的值来作为维护滑窗的依据,不满足要求时动滑窗的左端点直到符合题意。动态滑窗的长度就是要求的最大最小值。

例题2 — 1493.删掉一个元素以后全为一的最长子数组

给你一个二进制数组 nums ,你需要从中删掉一个元素。

请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。

如果不存在这样的子数组,请返回 0 。

提示 1:

输入:nums = [1,1,0,1]
输出:3
解释:删掉位置 2 的数后,[1,1,1] 包含 3 个 1 。

示例 2:

输入:nums = [0,1,1,1,0,1,1,0,1]
输出:5
解释:删掉位置 4 的数字后,[0,1,1,1,1,1,0,1] 的最长全 1 子数组为 [1,1,1,1,1] 。

示例 3:

输入:nums = [1,1,1]
输出:2
解释:你必须要删除一个元素。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 要么是 0 要么是 1 。
class Solution:def longestSubarray(self, nums: List[int]) -> int:cnt = left = ans = 0 #只需计算0的次数,不用单开数组节省空间for i, x in enumerate(nums):if x == 0:cnt += 1 if cnt > 1: #先判断是否需要移动左指针while cnt > 1 and left <= i: #常规循环变化left指针位置if nums[left] == 0:cnt -= 1left += 1ans = max(ans, i - left) #注意不要加一,因为最后答案要求不包含0ans = max(ans, i - left)return ans

#思路

与上题一样,通过while循环变换滑窗长度,动态求解最大值,只是这题不用计数多个字符,不用字典可以节约空间。

例题3 — 1208.尽可能使字符串相等

给你两个长度相同的字符串,s 和 t

将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。

用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。

如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。

如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0

示例 1:

输入:s = "abcd", t = "bcdf", maxCost = 3
输出:3
解释:s 中的 "abc" 可以变为 "bcd"。开销为 3,所以最大长度为 3。

示例 2:

输入:s = "abcd", t = "cdef", maxCost = 3
输出:1
解释:s 中的任一字符要想变成 t 中对应的字符,其开销都是 2。因此,最大长度为 1。

示例 3:

输入:s = "abcd", t = "acde", maxCost = 0
输出:1
解释:a -> a, cost = 0,字符串未发生变化,所以最大长度为 1。

提示:

  • 1 <= s.length, t.length <= 10^5
  • 0 <= maxCost <= 10^6
  • s 和 t 都只含小写英文字母。
class Solution:def equalSubstring(self, s: str, t: str, maxCost: int) -> int:left = ans = num = 0 #用变量来存动态判断值,节省空间for i, x in enumerate(s):num += abs(ord(s[i]) - ord(t[i])) #ASCLL码的差的绝对值while num > maxCost: #常规循环,通过num的值来做滑窗变化的判断num -= abs(ord(s[left]) - ord(t[left]))left += 1 ans = max(ans, i - left + 1)return ans

#思路

与写法与前几题大致一样,只是参杂了一些函数的变化。另外不要老是使用字典来存值,太浪费空间。这题还有前缀和的解法,具体看官方题解。前缀和的专题会在后面的文章提及。

例题4 — 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
class Solution:def totalFruit(self, fruits: List[int]) -> int:cnt = defaultdict(int)ans = left = 0for i, x in enumerate(fruits):cnt[x] += 1while len(cnt) > 2:cnt[fruits[left]] -= 1if cnt[fruits[left]] == 0:del cnt[fruits[left]] #要去掉为0的键,不然前面计算长度会出错left += 1ans = max(ans, i - left + 1)return ans

#思路

这题需要一点点的理解能力,做多能摘多少果子,其实就是求最长滑窗的长度(因为一棵树只能摘一个果子),其限制条件就是两个篮子(最多两个相同的数)。搞明白这两点后,就可以按照常规的不定长滑窗的题目来写。但由于不知道有几种水果,这题只能是用字典。通过字典长度来更新滑窗长度,所以要记得把值为0的键去掉。

例题5 — 最大连续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
class Solution:def longestOnes(self, nums: List[int], k: int) -> int:ans = left = cnt0 = 0for right, x in enumerate(nums):cnt0 += 1 - x  # 0 变成 1,用来统计 cnt0while cnt0 > k:cnt0 -= 1 - nums[left]left += 1ans = max(ans, right - left + 1) #循环结束前统一更新ans的值return ans

#思路

这题看着人畜无害短短的一点点,但实际上他和前面题目过程中有很大的不一样,我第一次写的时候也栽了,虽然最后改对了,但整体代码思路会变得比较复杂。上面给大家放的是别人的答案。

这题的陷阱就在于,当0计数到大于k时,ans要更新的值其实是i - left,但是如果0计数不超过k时,ans要更新的值就是i - left + 1,这个大家画个图就明白了。其实每次滑窗右移完都变化一次ans的值就可以解决这个问题(如上面的代码),但如果是顺着目前这个浅显的思维走,就要用两个if来解决,把这两种情况区分开,代码就会复杂些,如下(如果更惨的没看出来,那最后几个测试点你会死活都过不了)。

class Solution:def longestOnes(self, nums: List[int], k: int) -> int:cnt = ans = left = 0for i, x in enumerate(nums):if x == 0:cnt += 1if cnt > k:ans = max(ans, i - left)while cnt > k and left <= i:if nums[left] == 0:cnt -= 1left += 1continue #避免重复更新,下面的值会比上面的大,那if就没意义了ans = max(ans, i - left + 1)return ans

2、进阶部分

需要在传统的排序上多思考一些临界条件以及会参杂其他算法的一些思想(还有可能是数学的计算,那就回难一点),或者题目会比较难读懂,整体题目难度会比基础灵活不少(但也没有特别难)。

例题6 — 2730.找到最长的半重复子串

给你一个下标从 0 开始的字符串 s ,这个字符串只包含 0 到 9 的数字字符。

如果一个字符串 t 中至多有一对相邻字符是相等的,那么称这个字符串 t 是 半重复的 。例如,"0010" 、"002020" 、"0123" 、"2002" 和 "54944" 是半重复字符串,而 "00101022" (相邻的相同数字对是 00 和 22)和 "1101234883" (相邻的相同数字对是 11 和 88)不是半重复字符串。

请你返回 s 中最长 半重复 子字符串 的长度。

示例 1:

输入:s = "52233"

输出:4

解释:

最长的半重复子字符串是 "5223"。整个字符串 "52233" 有两个相邻的相同数字对 22 和 33,但最多只能选取一个。

示例 2:

输入:s = "5494"

输出:4

解释:

s 是一个半重复字符串。

示例 3:

输入:s = "1111111"

输出:2

解释:

最长的半重复子字符串是 "11"。子字符串 "111" 有两个相邻的相同数字对,但最多允许选取一个。

提示:

  • 1 <= s.length <= 50
  • '0' <= s[i] <= '9'
class Solution:def longestSemiRepetitiveSubstring(self, s: str) -> int:left = ans = res = cnt = 0 #初始化cnt用来标记上次连续字符的位置for i in range(len(s)):if i == len(s) - 1: #防止数组越界ans = max(ans, i - left + 1)continueif s[i] == s[i + 1] and res == 0:res = 1 #第一次相等的情况cnt = i + 1ans = max(ans, i - left + 1)continueif s[i] == s[i + 1] and res == 1: #后续相等情况ans = max(ans, i - left + 1)left = cnt #left接到上次相等的位置,res值不变,此时滑窗中必然有连续cnt = i + 1continueans = max(ans, i - left + 1) #除去上述条件,ans的值要更新,不然会漏答案return ans  

#思路

我第一次写的时候,选择按照情况来分类写,比较直观,但是这些情况其实都是题目的坑。特别是cnt这个变量,大家容易想当然直接让left=i,这就错的很离谱了。

下面的代码是另一种写法,也是标准的while语句滑窗,大家也可以参考一下。

以下是它的思路

class Solution:def longestSemiRepetitiveSubstring(self, s: str) -> int:ans, left, same = 1, 0, 0for right in range(1, len(s)):same += s[right] == s[right - 1]if same > 1:  # same == 2left += 1while s[left] != s[left - 1]:left += 1same = 1ans = max(ans, right - left + 1)return ans

例题7 — 2779.数组的最大美丽值

给你一个下标从 0 开始的整数数组 nums 和一个 非负 整数 k 。

在一步操作中,你可以执行下述指令:

  • 在范围 [0, nums.length - 1] 中选择一个 此前没有选过 的下标 i 。
  • 将 nums[i] 替换为范围 [nums[i] - k, nums[i] + k] 内的任一整数。

数组的 美丽值 定义为数组中由相等元素组成的最长子序列的长度。

对数组 nums 执行上述操作任意次后,返回数组可能取得的 最大 美丽值。

注意:你  能对每个下标执行 一次 此操作。

数组的 子序列 定义是:经由原数组删除一些元素(也可能不删除)得到的一个新数组,且在此过程中剩余元素的顺序不发生改变。

示例 1:

输入:nums = [4,6,1,2], k = 2
输出:3
解释:在这个示例中,我们执行下述操作:
- 选择下标 1 ,将其替换为 4(从范围 [4,8] 中选出),此时 nums = [4,4,1,2] 。
- 选择下标 3 ,将其替换为 4(从范围 [0,4] 中选出),此时 nums = [4,4,1,4] 。
执行上述操作后,数组的美丽值是 3(子序列由下标 0 、1 、3 对应的元素组成)。
可以证明 3 是我们可以得到的由相等元素组成的最长子序列长度。

示例 2:

输入:nums = [1,1,1,1], k = 10
输出:4
解释:在这个示例中,我们无需执行任何操作。
数组 nums 的美丽值是 4(整个数组)。

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i], k <= 105
class Solution:def maximumBeauty(self, nums: List[int], k: int) -> int:nums.sort() #原地排序,因为题目没说要按顺序left = ans = 0for i, x in enumerate(nums):while 2 * k < x - nums[left]: #数学转换left += 1ans = max(ans, i - left + 1)return ans

#思路

这题乍一看就很多,实际上它确实不简单。需要一点数学想法,再加上把原本的数组转化为可以滑窗的数组,我一开始写的时候也是没什么想法,直到看了一眼别人的图才恍然大悟。

这里放一下大佬的详细题解,我的思路和他差不多,个别参数不一样大家应该也能看懂,毕竟他写的挺细致的

“由于选的是子序列,且操作后子序列的元素都相等,所以元素顺序对答案没有影响,可以先对数组排序。

示例 1 排序后 nums=[1,2,4,6]。由于每个数 x 可以改成闭区间 [x−k,x+k] 中的数,我们把示例 1 的每个数看成闭区间,也就是

题目要求的「由相等元素组成的最长子序列」,相当于选出若干闭区间,这些区间的交集不为空。

排序后,选出的区间是连续的,我们只需考虑最左边的区间 [x−k,x+k] 和最右边的区间 [y−k,y+k],如果这两个区间的交集不为空,那么选出的这些区间的交集就不为空。也就是说,要满足

x+k≥y−k

y−x≤2k
于是原问题等价于:

排序后,找最长的连续子数组,其最大值减最小值 ≤2k。由于数组是有序的,相当于子数组的最后一个数减去子数组的第一个数 ≤2k。
只要子数组满足这个要求,对应的区间的交集就不为空,也就是子数组的元素都可以变成同一个数。

这可以用 滑动窗口 解决。枚举 nums[right] 作为子数组的最后一个数,一旦 nums[right]−nums[left]>2k,就移动左端点 left。

左端点停止移动时,下标在 [left,right] 的子数组就是满足要求的子数组,用子数组长度 right−left+1 更新答案的最大值。”

例题8 — 2516.每种字符至少取k个

给你一个由字符 'a''b''c' 组成的字符串 s 和一个非负整数 k 。每分钟,你可以选择取走 s 最左侧 还是 最右侧 的那个字符。

你必须取走每种字符 至少 k 个,返回需要的 最少 分钟数;如果无法取到,则返回 -1 。

示例 1:

输入:s = "aabaaaacaabc", k = 2
输出:8
解释:
从 s 的左侧取三个字符,现在共取到两个字符 'a' 、一个字符 'b' 。
从 s 的右侧取五个字符,现在共取到四个字符 'a' 、两个字符 'b' 和两个字符 'c' 。
共需要 3 + 5 = 8 分钟。
可以证明需要的最少分钟数是 8 。

示例 2:

输入:s = "a", k = 1
输出:-1
解释:无法取到一个字符 'b' 或者 'c',所以返回 -1 。

提示:

  • 1 <= s.length <= 105
  • s 仅由字母 'a''b''c' 组成
  • 0 <= k <= s.length
class Solution:def takeCharacters(self, s: str, k: int) -> int:cnt = Counter(s) #计算总的abc字符的值,这里返回一个字典if any(cnt[c] < k for c in 'abc' ): #python的便捷写法return -1ans = left = 0for i, x in enumerate(s): #用滑窗的方法求解最大剩余子串cnt[x] -= 1while cnt[x] < k: #字典内是要取走的字符,不够就要移动滑窗还回字符cnt[s[left]] += 1left += 1ans = max(ans, i - left + 1)return len(s) - ans #与总长相减就是答案

#思路

经典的逆向思维的题目,正着取走不好算,就反过来计算留下的长度,写多了一眼就能看出来。只要思想上绕过来这个弯这题就简单了。

比如 s 中有 3 个 a,4 个 b,5 个 c,k=2,每种字母至少取走 2 个,等价于剩下的字母至多有 1 个 a,2 个 b 和 3 个 c。

由于只能从 s 最左侧和最右侧取走字母,所以剩下的字母是 s 的子串。

设 s 中的 a,b,c 的个数分别为 x,y,z,现在问题变成:

计算 s 的最长子串长度,该子串满足 a,b,c 的个数分别至多为 x−k,y−k,z−k。
由于子串越短越能满足要求,越长越不能满足要求,有单调性,可以用滑动窗口解决。

实现思路

与其维护窗口内的字母个数,不如直接维护窗口外的字母个数,这也是我们取走的字母个数。

一开始,假设我们取走了所有的字母。或者说,初始窗口是空的,窗口外的字母个数就是 s 的每个字母的出现次数。
右端点字母进入窗口后,该字母取走的个数减一。
如果减一后,窗口外该字母的个数小于 k,说明子串太长了,或者取走的字母个数太少了,那么就不断右移左端点,把左端点字母移出窗口,相当于我们取走移出窗口的字母,直到该字母个数等于 k,退出内层循环。
内层循环结束后,用窗口长度 right−left+1 更新子串长度的最大值。
最后,原问题的答案为 n 减去子串长度的最大值。

特别地,如果 s 中某个字母的个数不足 k,那么无法满足题目要求,返回 −1。

例题9 — 2271.毯子覆盖的最多白色砖块数

给你一个二维整数数组 tiles ,其中 tiles[i] = [li, ri] ,表示所有在 li <= j <= ri 之间的每个瓷砖位置 j 都被涂成了白色。

同时给你一个整数 carpetLen ,表示可以放在 任何位置 的一块毯子的长度。

请你返回使用这块毯子,最多 可以盖住多少块瓷砖。

示例 1:

输入:tiles = [[1,5],[10,11],[12,18],[20,25],[30,32]], carpetLen = 10
输出:9
解释:将毯子从瓷砖 10 开始放置。
总共覆盖 9 块瓷砖,所以返回 9 。
注意可能有其他方案也可以覆盖 9 块瓷砖。
可以看出,瓷砖无法覆盖超过 9 块瓷砖。

示例 2:

输入:tiles = [[10,11],[1,1]], carpetLen = 2
输出:2
解释:将毯子从瓷砖 10 开始放置。
总共覆盖 2 块瓷砖,所以我们返回 2 。

提示:

  • 1 <= tiles.length <= 5 * 104
  • tiles[i].length == 2
  • 1 <= li <= ri <= 109
  • 1 <= carpetLen <= 109
  • tiles 互相 不会重叠 。
class Solution:def maximumWhiteTiles(self, tiles: List[List[int]], carpetLen: int) -> int:tiles.sort(key = lambda x : x[0])#对原本二维数组排序,方便后续处理ans = res = left = temp = 0#初始化,这题可以说是滑窗也可以说是双指针,但其实两者本质上就没有什么太大的差别for p, q in tiles:res += q - p + 1#把数组里面每段白色的值先加上while q - carpetLen + 1 > tiles[left][1]:#当毯子左端点大于左指针所在的片段的右端点时,说明滑窗太长了res -= tiles[left][1] - tiles[left][0] + 1#扣掉left移动后移出滑窗的白色区域left += 1temp = max(q - carpetLen + 1 - tiles[left][0], 0)#计算未覆盖的区域,由于一开始毯子还没有完全移进来,所以是负数,不能减#这里是拿毯子的左端点减掉left指针在的白色区域的左端点,去掉未覆盖的区域#毯子的右端点一直和白色区域的右端点重合ans = max(ans, res - temp)return ans

#思路

这题确实是有点上强度了,有点难度并且不好理解。如果不考虑空间的情况下,是可以再搞一个数组出来,然后把题目变成标准的滑窗,但是那样复杂度就高了。如果直接在这个二维数组上运行,又不能直接整个毯子放上去,那样还要提前考虑后面可能的n个区域是否被覆盖,也很麻烦。这里提供一个大佬的思路,他是始终将毯子的右端点和白色区域的右端点重合,一点点把毯子放上去,把毯子作为一个定长滑窗,然后外侧套了一个双指针来更新白色区域被覆盖数的写法。

首先,将 tiles 按左端点 li
排序后,我们可以枚举毯子的摆放位置,然后计算毯子能覆盖多少块瓷砖。

实际上,毯子右端点放在一段瓷砖中间,是不如直接放在这段瓷砖右端点的(因为从中间向右移动,能覆盖的瓷砖数不会减少),所以可以枚举每段瓷砖的右端点来摆放毯子的右端点。

这样就可以双指针了,左指针 left 需要满足其指向的那段瓷砖的右端点被毯子覆盖。

关键点

设毯子右端点在瓷砖段 i 上,则毯子左端点位于 tiles[i][1]−carpetLen+1,对于 left 需要满足

tiles[left][1]≥tiles[i][1]−carpetLen+1
如果毯子左端点在瓷砖段 tiles[left] 内部,则覆盖的瓷砖数还需要额外减去这段瓷砖没被覆盖的部分,即减去

(tiles[i][1]−carpetLen+1)−tiles[left][0]
如果上式是负数则不减。

例题10 — 2106.摘水果(困难)

在一个无限的 x 坐标轴上,有许多水果分布在其中某些位置。给你一个二维整数数组 fruits ,其中 fruits[i] = [positioni, amounti] 表示共有 amounti 个水果放置在 positioni 上。fruits 已经按 positioni 升序排列 ,每个 positioni 互不相同 。

另给你两个整数 startPos 和 k 。最初,你位于 startPos 。从任何位置,你可以选择 向左或者向右 走。在 x 轴上每移动 一个单位 ,就记作 一步 。你总共可以走 最多 k 步。你每达到一个位置,都会摘掉全部的水果,水果也将从该位置消失(不会再生)。

返回你可以摘到水果的 最大总数 。

示例 1:

输入:fruits = [[2,8],[6,3],[8,6]], startPos = 5, k = 4
输出:9
解释:
最佳路线为:
- 向右移动到位置 6 ,摘到 3 个水果
- 向右移动到位置 8 ,摘到 6 个水果
移动 3 步,共摘到 3 + 6 = 9 个水果

示例 2:

输入:fruits = [[0,9],[4,1],[5,7],[6,2],[7,4],[10,9]], startPos = 5, k = 4
输出:14
解释:
可以移动最多 k = 4 步,所以无法到达位置 0 和位置 10 。
最佳路线为:
- 在初始位置 5 ,摘到 7 个水果
- 向左移动到位置 4 ,摘到 1 个水果
- 向右移动到位置 6 ,摘到 2 个水果
- 向右移动到位置 7 ,摘到 4 个水果
移动 1 + 3 = 4 步,共摘到 7 + 1 + 2 + 4 = 14 个水果

示例 3:

输入:fruits = [[0,3],[6,4],[8,5]], startPos = 3, k = 2
输出:0
解释:
最多可以移动 k = 2 步,无法到达任一有水果的地方

提示:

  • 1 <= fruits.length <= 105
  • fruits[i].length == 2
  • 0 <= startPos, positioni <= 2 * 105
  • 对于任意 i > 0 ,positioni-1 < positioni 均成立(下标从 0 开始计数)
  • 1 <= amounti <= 104
  • 0 <= k <= 2 * 105

class Solution:def maxTotalFruits(self, fruits: List[List[int]], startPos: int, k: int) -> int:#初始化左右两侧滑窗位置,先从向左走到底的方法#bisect-用二分查找来在有序列表中快速定位“插入点”,#bisect_left(a, x)返回第一个 i,使得 a[i] ≥ x;#也就是把 x 插到所有 == x 的元素 最左边。left = bisect_left(fruits, [startPos - k])right = bisect_left(fruits, [startPos + 1])ans = s = sum(c for _, c in fruits[left : right])#开始遍历,不断将新增数字加入while right < len(fruits) and fruits[right][0] <= startPos + k:s += fruits[right][1]#判断条件:无论先向左还是先向右,步数都大于k,则左侧滑窗端点移动while fruits[right][0] * 2 - fruits[left][0] - startPos > k and 
fruits[right][0] - fruits[left][0] * 2 + startPos > k:s -= fruits[left][1]left += 1ans = max(ans, s)right += 1return ans            

#思路

这题是在上一题写法上的进一步扩展,也是以离散点的形式给出滑窗的基本条件。如果仅仅用基础的不定长滑窗,将离散点转化成连续列表,那样时间复杂度会非常非常大,连第二个测试点都跑不完。

因为走的时候虽然可以来回走,但是走的过程一定是连续的,所以此题也可以用滑窗来解决。这里通过一个正确思路供大家参考(配合代码注释看的更明白)

‘由于只能一步步地走,人移动的范围必然是一段连续的区间。

如果反复左右移动,会白白浪费移动次数,所以最优方案要么先向右再向左,要么先向左再向右(或者向一个方向走到底)。

设向左走最远可以到达 fruits[left][0],这可以用枚举或者二分查找得出,其中 left 是最小的满足

fruits[left][0]≥startPos−k的下标。

假设位置不超过 startPos 的最近水果在 fruits[right][0],那么当 right 增加时,left 不可能减少,有单调性,因此可以用同向双指针(滑动窗口)解决。

如何判断 left 是否需要增加呢?

如果先向右再向左,那么移动距离为

(fruits[right][0]−startPos)+(fruits[right][0]−fruits[left][0])
如果先向左再向右,那么移动距离为

(startPos−fruits[left][0])+(fruits[right][0]−fruits[left][0])
如果上面两个式子均大于 k,就说明 fruits[left][0] 太远了,需要增加 left。

对于 right,它必须小于 n,且满足

fruits[right][0]≤startPos+k
移动 left 和 right 的同时,维护窗口内的水果数量 s,同时用 s 更新答案的最大值。’

例题11 — 2555.两个线段获得的最多奖品(动态规划+前缀和)

在 X轴 上有一些奖品。给你一个整数数组 prizePositions ,它按照 非递减 顺序排列,其中 prizePositions[i] 是第 i 件奖品的位置。数轴上一个位置可能会有多件奖品。再给你一个整数 k 。

你可以同时选择两个端点为整数的线段。每个线段的长度都必须是 k 。你可以获得位置在任一线段上的所有奖品(包括线段的两个端点)。注意,两个线段可能会有相交。

  • 比方说 k = 2 ,你可以选择线段 [1, 3] 和 [2, 4] ,你可以获得满足 1 <= prizePositions[i] <= 3 或者 2 <= prizePositions[i] <= 4 的所有奖品 i 。

请你返回在选择两个最优线段的前提下,可以获得的 最多 奖品数目。

示例 1:

输入:prizePositions = [1,1,2,2,3,3,5], k = 2
输出:7
解释:这个例子中,你可以选择线段 [1, 3] 和 [3, 5] ,获得 7 个奖品。

示例 2:

输入:prizePositions = [1,2,3,4], k = 0
输出:2
解释:这个例子中,一个选择是选择线段 [3, 3][4, 4] ,获得 2 个奖品。

提示:

  • 1 <= prizePositions.length <= 105
  • 1 <= prizePositions[i] <= 109
  • 0 <= k <= 109
  • prizePositions 有序非递减。
class Solution:def maximizeWin(self, prizePositions: List[int], k: int) -> int:n = len(prizePositions)#如果奖品数组长度比两个线段长度小或等,说明一定可以全覆盖,可以简化计算直接返回if 2 * k + 1 >= prizePositions[n - 1] - prizePositions[0]:return nans = left = 0#用前缀和与动态规划的思想,假设mx[i]是前i部分线段可以取得的最大值mx = [0] * (n +1)for right, p in enumerate(prizePositions):#确保滑窗长度合法while p - prizePositions[left] > k:left += 1#mx[left]是左侧滑窗的值ans = max(ans, mx[left] + right - left + 1)#更新mx,利用动态规划思想,要么不取新的点,要么取,就看谁大mx[right + 1] = max(mx[right], right - left + 1)return ans

#思路

这题稍微有点变化,虽然也是离散点值,但难度不在滑窗遍历上,而在于如何安排两个可能连续又重合或者独立的线段。很明显因为不一定连续,所以不能把其合并成一个滑窗来计算。但是,换种角度想,两个滑窗其实是一样的长度,也就是说,前者遍历过的结果在后者也适用,就联想到前缀和并用动态规划更新。并且我们贪心一点想,一定是两个滑窗各自独立才会覆盖最大范围,从概率角度出发,这样一定是取到最大值。

参考了一份题解的思路,大家可以配合代码注释理解

两条线段一左一右。考虑枚举右(第二条线段),同时维护左(第一条线段)能覆盖的最多奖品个数。

贪心地想,两条线段不相交肯定比相交更好,覆盖的奖品可能更多。

设第二条线段右端点在 prizePositions[right] 时,最远(最小)覆盖的奖品的位置为 prizePositions[left]。

我们需要计算在 prizePositions[left] 左侧的第一条线段最多可以覆盖多少个奖品。这可以保证两条线段不相交。

定义 mx[i+1] 表示第一条线段右端点 ≤prizePositions[i] 时,最多可以覆盖多少个奖品。特别地,定义 mx[0]=0。

如何计算 mx?

考虑动态规划:

线段右端点等于 prizePositions[i] 时,可以覆盖最多的奖品,即 i−left +1。其中 left 表示右端点覆盖奖品 prizePositions[i] 时,最左边的被线段覆盖的奖品。
线段右端点小于 prizePositions[i] 时,可以覆盖最多的奖品,这等价于右端点 ≤prizePositions[i−1] 时,最多可以覆盖多少个奖品,即 mx[i]。注:这里可以说明为什么状态要定义成 mx[i+1] 而不是 mx[i],这可以避免当 i=0 时出现 i−1=−1 这种情况。
二者取最大值,得

mx[i+1]=max(mx[i],i−left +1)
上式也可以理解为 i−left +1 的前缀最大值。

如何计算两条线段可以覆盖的奖品个数?

第二条线段覆盖的奖品个数为 right−left+1。
第一条线段覆盖的奖品个数为线段右端点 ≤prizePositions[left−1] 时,最多覆盖的奖品个数,即 mx[left]。
综上,两条线段可以覆盖的奖品个数为

mx[left]+right−left+1
枚举 right 的过程中,取上式的最大值,即为答案。

我们遍历了所有的奖品作为第二条线段的右端点,通过 mx[left] 保证第一条线段与第二条线段不相交,且第一条线段覆盖了第二条线段左侧的最多奖品。那么这样遍历后,算出的答案就一定是所有情况中的最大值。

⚠注意:可以在计算第二条线段的滑动窗口的同时,更新和第一条线段有关的 mx。这是因为两条线段一样长,第二条线段移动到 right 时所覆盖的奖品个数,也是第一条线段移动到 right 时所覆盖的奖品个数。

小优化:如果 2k+1≥prizePositions[n−1]−prizePositions[0],说明所有奖品都可以被覆盖,直接返回 n。例如 prizePositions=[0,1,2,3], k=1,那么第一条线段覆盖 0 和 1,第二条线段覆盖 2 和 3,即可覆盖所有奖品。

例题12 — 2009.使数组连续的最少操作数(困难)

相关企业

提示

给你一个整数数组 nums 。每一次操作中,你可以将 nums 中 任意 一个元素替换成 任意 整数。

如果 nums 满足以下条件,那么它是 连续的 :

  • nums 中所有元素都是 互不相同 的。
  • nums 中 最大 元素与 最小 元素的差等于 nums.length - 1 。

比方说,nums = [4, 2, 5, 3] 是 连续的 ,但是 nums = [1, 2, 3, 5, 6] 不是连续的 。

请你返回使 nums 连续 的 最少 操作次数。

示例 1:

输入:nums = [4,2,5,3]
输出:0
解释:nums 已经是连续的了。

示例 2:

输入:nums = [1,2,3,5,6]
输出:1
解释:一个可能的解是将最后一个元素变为 4 。
结果数组为 [1,2,3,5,4] ,是连续数组。

示例 3:

输入:nums = [1,10,100,1000]
输出:3
解释:一个可能的解是:
- 将第二个元素变为 2 。
- 将第三个元素变为 3 。
- 将第四个元素变为 4 。
结果数组为 [1,2,3,4] ,是连续数组。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
class Solution:def minOperations(self, nums: List[int]) -> int:n = len(nums)#给出排序后的新数组arrarr = sorted(set(nums))left = ans = 0#这里注意是在排序后的数组上操作for i, c in enumerate(arr):#对nums的值进行判断,滑窗内连续的数长度是否满足小于n#测的是计算的值while arr[left] < c - n + 1:left += 1#更新ans的值,用的是arr的索引说明此时有几个数#更新的是实际的数ans = max(ans, i - left + 1)#扣掉最大原本就可以连续的部分,剩下的就是需要调整的部分return n - ans

#思路

经典的“正难则反”问题,和前面的逆向思维题目一样的思想。因为操作次数与顺序无关,可以对数组进行排序操作,此时不难看出,需要改变的均是数组两次的值,而中间的全是连续数组,可以使用滑窗,故此可以联想到对最大不用操作的求解。但是和传统滑窗不一样的点在于,滑窗更新的两个值,一个下标一个数值,都有不一样的含义。数值表示连续数的范围目前有几个数,属于抽象取值,是滑窗移动条件;而下标值表示实际的连续数数量,要拿来做实际计算更新答案。这里选取了一个简洁明了的思路,供大家参考。

“正难则反,考虑最多保留多少个元素不变。

设 x 是修改后的连续数字的最大值,则修改后的连续数字的范围为闭区间 [x−n+1,x],其中 n 是 nums 的长度。在修改前,对于已经在 [x−n+1,x] 中的数,我们无需修改。那么,x 取多少,可以让无需修改的数最多呢?

由于元素的位置不影响答案,且要求所有元素互不相同,我们可以将 nums 从小到大排序,并去掉重复元素。设 a 为 nums 排序去重后的数组。把 a[i] 画在一条数轴上,本题相当于有一个长度为 n 的滑动窗口,我们需要计算窗口内最多可以包含多少个数轴上的点。

定理:只需要枚举 a[i] 作为窗口的右端点。

证明:在窗口从左向右滑动的过程中,如果窗口右端点处没有点,那么继续滑动,在滑到下一个点之前,窗口内包含的点的个数是不会增多的。

为了算出窗口内有多少个点,我们需要知道窗口包含的最左边的点在哪,设这个点的位置是 a[left],则它必须大于等于窗口的左边界,即

a[left]≥a[i]−n+1
此时窗口内有 i−left+1 个点,取其最大值,就得到了最多保留不变的元素个数。最后用 n 减去保留不变的元素个数,就得到了答案。”

配合代码注释观看更佳

二、求最小、最短

题目大多会有最短/最小的要求,或者隐含的要求

例题13 — 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] <= 104
class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:n = len(nums)ans = n + 1 #也可以设为无穷大res = left = 0for i, x in enumerate(nums):res += x#当res满足条件时就更新ans的值while res >= target:ans = min(ans, i - left + 1)res -= nums[left]left += 1#如果ans值无变化就返回0return ans if ans < n + 1 else 0

#思路

按照题目要求用代码翻译即可,求最小子数组,连续部分一眼看出是滑窗解法,通过窗口不断遍历更新答案。在res第一次大于target的时候就直接while循环更新,就不会错漏。我是顺着思路写的,直接在循环中更新ans的值,也可以在循环外,但代码和逻辑上就复杂些,虽然速度会快。

例题14 — 2904.最短且字典序最小的子字符串

给你一个二进制字符串 s 和一个正整数 k 。

如果 s 的某个子字符串中 1 的个数恰好等于 k ,则称这个子字符串是一个 美丽子字符串 。

令 len 等于 最短 美丽子字符串的长度。

返回长度等于 len 且字典序 最小 的美丽子字符串。如果 s 中不含美丽子字符串,则返回一个  字符串。

对于相同长度的两个字符串 a 和 b ,如果在 a 和 b 出现不同的第一个位置上,a 中该位置上的字符严格大于 b 中的对应字符,则认为字符串 a 字典序 大于 字符串 b 。

  • 例如,"abcd" 的字典序大于 "abcc" ,因为两个字符串出现不同的第一个位置对应第四个字符,而 d 大于 c 。

示例 1:

输入:s = "100011001", k = 3
输出:"11001"
解释:示例中共有 7 个美丽子字符串:
1. 子字符串 "100011001" 。
2. 子字符串 "100011001" 。
3. 子字符串 "100011001" 。
4. 子字符串 "100011001" 。
5. 子字符串 "100011001" 。
6. 子字符串 "100011001" 。
7. 子字符串 "100011001" 。
最短美丽子字符串的长度是 5 。
长度为 5 且字典序最小的美丽子字符串是子字符串 "11001" 。

示例 2:

输入:s = "1011", k = 2
输出:"11"
解释:示例中共有 3 个美丽子字符串:
1. 子字符串 "1011" 。
2. 子字符串 "1011" 。
3. 子字符串 "1011" 。
最短美丽子字符串的长度是 2 。
长度为 2 且字典序最小的美丽子字符串是子字符串 "11" 。 

示例 3:

输入:s = "000", k = 1
输出:""
解释:示例中不存在美丽子字符串。

提示:

  • 1 <= s.length <= 100
  • 1 <= k <= s.length
class Solution:def shortestBeautifulSubstring(self, s: str, k: int) -> str:#如果‘1’的和全部加起来都比k小,那就不用进入下面的循环判断了if s.count('1') < k:return ''left = res = 0ans = s #返回的值是字符串不是数字for i, x in enumerate(s):res += int(x) #将字符串转为整型才能数值相加# 在左端点处‘0’应该直接排出滑窗,才能保证最短while res > k or s[left] == '0':res -= int(s[left])left += 1if res == k:temp = s[left: i + 1]# 长度是第一条件,在相等情况下再看字典序,’or‘就是两个条件的分界if len(temp) < len(ans) or len(temp) == len(ans) and temp < ans:ans = tempreturn ans

#思路

由于答案中恰好有 k 个 1,我们可以用滑动窗口找最短的答案。

如果窗口内的 1 的个数超过 k,或者窗口端点是 0,就可以缩小窗口。

注:利用字符串哈希(或者后缀数组等),可以把比较字典序的时间降至 O(nlogn),这样可以做到 O(nlogn) 的时间复杂度。

例题15 — 1234.替换子串得到平衡字符串

有一个只含有 'Q', 'W', 'E', 'R' 四种字符,且长度为 n 的字符串。

假如在该字符串中,这四个字符都恰好出现 n/4 次,那么它就是一个「平衡字符串」。

给你一个这样的字符串 s,请通过「替换一个子串」的方式,使原字符串 s 变成一个「平衡字符串」。

你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。

请返回待替换子串的最小可能长度。

如果原字符串自身就是一个平衡字符串,则返回 0

示例 1:

输入:s = "QWER"
输出:0
解释:s 已经是平衡的了。

示例 2:

输入:s = "QQWE"
输出:1
解释:我们需要把一个 'Q' 替换成 'R',这样得到的 "RQWE" (或 "QRWE") 是平衡的。

示例 3:

输入:s = "QQQW"
输出:2
解释:我们可以把前面的 "QQ" 替换成 "ER"。 

示例 4:

输入:s = "QQQQ"
输出:3
解释:我们可以替换后 3 个 'Q',使 s = "QWER"。

提示:

  • 1 <= s.length <= 10^5
  • s.length 是 4 的倍数
  • s 中只含有 'Q''W''E''R' 四种字符
class Solution:def balancedString(self, s: str) -> int:m = len(s) // 4# 计算每种字符有几个,会形成一个字典dicts = Counter(s)# 当字典里面有四个字符且最大值或者最小值为m时就已经符合条件了if len(dicts) == 4 and max(dicts.values()) == m:return 0ans = len(s) + 1left = 0for i, x in enumerate(s):dicts[x] -= 1#循环判断,当最大的字典值小于等于m时有可能是正确答案while max(dicts.values()) <= m:ans = min(ans, i - left + 1)#要恢复之前变化的字典值在移动滑窗左端点前dicts[s[left]] += 1left += 1return ans

#思路

根据题意,如果在待替换子串之外的任意字符的出现次数超过 m= n/4
 ,那么无论怎么替换,都无法使这个字符在整个字符串中的出现次数为 m。

反过来说,如果在待替换子串之外的任意字符的出现次数都不超过 m,那么可以通过替换,使 s 为平衡字符串,即每个字符的出现次数均为 m。

这可以用滑动窗口实现.

对于本题,设子串的左右端点为 left 和 right,枚举 right,如果子串外的任意字符的出现次数都不超过 m,则说明从 left 到 right 的这段子串可以是待替换子串,用其长度 right−left+1 更新答案的最小值,并向右移动 left,缩小子串长度。

例题16 — 2875.无限数组的最短子数组

给你一个下标从 0 开始的数组 nums 和一个整数 target 。

下标从 0 开始的数组 infinite_nums 是通过无限地将 nums 的元素追加到自己之后生成的。

请你从 infinite_nums 中找出满足 元素和 等于 target 的 最短 子数组,并返回该子数组的长度。如果不存在满足条件的子数组,返回 -1 。

示例 1:

输入:nums = [1,2,3], target = 5
输出:2
解释:在这个例子中 infinite_nums = [1,2,3,1,2,3,1,2,...] 。
区间 [1,2] 内的子数组的元素和等于 target = 5 ,且长度 length = 2 。
可以证明,当元素和等于目标值 target = 5 时,2 是子数组的最短长度。

示例 2:

输入:nums = [1,1,1,2,3], target = 4
输出:2
解释:在这个例子中 infinite_nums = [1,1,1,2,3,1,1,1,2,3,1,1,...].
区间 [4,5] 内的子数组的元素和等于 target = 4 ,且长度 length = 2 。
可以证明,当元素和等于目标值 target = 4 时,2 是子数组的最短长度。

示例 3:

输入:nums = [2,4,6,8], target = 3
输出:-1
解释:在这个例子中 infinite_nums = [2,4,6,8,2,4,6,8,...] 。
可以证明,不存在元素和等于目标值 target = 3 的子数组。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105
  • 1 <= target <= 109
class Solution:def minSizeSubarray(self, nums: List[int], target: int) -> int:n = len(nums)left = res = 0ans = 2 * n + 1#计算需要遍历的部分temp = target % sum(nums)#因为取的余数,所以最多不会使用超过两倍长的数组#滑窗常规写法for i in range(2 * n):res += nums[i % n]while res > temp:res -= nums[left % n]left += 1#只有相等才更新if res == temp:#这里可以保证当temp = 0的时候,ans的值也会更新为0ans = min(ans, i - left + 1)#记得-1的情况和加上k个完整数组长度return ans + target // sum(nums) * n if ans < 2 * n + 1 else -1

#思路

这题难点不在滑窗,在于怎么处理最后的边界。如果循环暴力从头遍历到尾,会浪费很多空间和时间。如果只是常数级遍历,可能会漏掉很多情况。所以就需要先讲target进行数学变换,再滑窗求解。下面是具体思路,仅供参考。

三、结尾

这是滑窗部分的第二节学习笔记,题目不少,大家可以根据需要自取学习。下一篇可能要到七月份再出了。有问题欢迎评论区提出。上述所有代码思路都不唯一,只是个人觉得还不错的解法,大家可以自己思考或者去题解区寻找更优解或者更容易理解的解法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/84064.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node.js Express 项目现代化打包部署全指南

Node.js Express 项目现代化打包部署全指南 一、项目准备阶段 1.1 依赖管理优化 # 生产依赖安装&#xff08;示例&#xff09; npm install express mongoose dotenv compression helmet# 开发依赖安装 npm install nodemon eslint types/node --save-dev1.2 环境变量配置 /…

Linux电源管理——PSCI初始化流程和多核启动流程

目录 一、PSCI 初始化流程 1、PSCI设备树节点 2、PSCI kernel初始化流程 get_set_conduit_method set_conduit psci_probe 二、CPU PSCI 操作初始化流程 1、CPU 设备树节点 2、 struct cpu_operations 3、kernel 流程 cpu_read_bootcpu_ops smp_init_cpus 三、CPU…

【Nginx学习笔记】:Fastapi服务部署单机Nginx配置说明

服务部署单机Nginx配置说明 服务.conf配置文件&#xff1a; upstream asr_backend {server 127.0.0.1:8010; }server {listen 80;server_name your_domain.com;location / {proxy_pass http://localhost:8000;proxy_set_header Host $host;proxy_set_header X-Real-IP $remot…

nfs存储IO等待,导致k8s业务系统卡慢问题处理

注:服务器配置:64C,128G,麒麟v10系统,系统磁盘使用空间(5T)均低于50%,存储磁盘iops约为800左右 发现业务系统卡慢,使用top 命令查看.系统负载较高长期保持在60以上,发现wa值的指标参数长期高于15,返现CPU用于写入磁盘IO等待的时间较高,系统的磁盘I/O压力较大. 配合开发查看日志…

Pytorch 项目实战-1: MNIST 手写数字识别

刚接触深度学习的小伙伴们&#xff0c;是不是经常听说 MNIST 数据集和 PyTorch 框架&#xff1f;今天就带大家从零开始&#xff0c;用 PyTorch 实现 MNIST 手写数字识别&#xff0c;轻松迈出深度学习实践的第一步&#xff01; 一、MNIST 数据集&#xff1a;深度学习界的 “Hel…

大数据量下Redis分片的5种策略

随着业务规模的增长&#xff0c;单一Redis实例面临着内存容量、网络带宽和计算能力的瓶颈。 分片(Sharding)成为扩展Redis的关键策略&#xff0c;它将数据分散到多个Redis节点上&#xff0c;每个节点负责整个数据集的一个子集。 本文将分享5种Redis分片策略。 1. 取模分片(M…

CentOS 7上搭建高可用BIND9集群指南

在 CentOS 7 上搭建一个高可用的 BIND9 集群通常涉及以下几种关键技术和策略的组合&#xff1a;主从复制 (Master-Slave Replication)、负载均衡 (Load Balancing) 以及可能的浮动 IP (Floating IP) 或 Anycast。 我们将主要关注主从复制和负载均衡的实现&#xff0c;这是构成高…

LangChain4j入门AI(六)整合提示词(Prompt)

前言 提示词&#xff08;Prompt&#xff09;是用户输入给AI模型的一段文字或指令&#xff0c;用于引导模型生成特定类型的内容。通过提示词&#xff0c;用户可以告诉AI“做什么”、 “如何做”以及“输出格式”&#xff0c;从而在满足需求的同时最大程度减少无关信息的生成。有…

【MySQL】笔记

&#x1f4da; 博主的专栏 &#x1f427; Linux | &#x1f5a5;️ C | &#x1f4ca; 数据结构 | &#x1f4a1;C 算法 | &#x1f152; C 语言 | &#x1f310; 计算机网络 在ubuntu中&#xff0c;改配置文件&#xff1a; sudo nano /etc/mysql/mysql.conf.d/mysq…

TDengine 运维—容量规划

概述 若计划使用 TDengine 搭建一个时序数据平台&#xff0c;须提前对计算资源、存储资源和网络资源进行详细规划&#xff0c;以确保满足业务场景的需求。通常 TDengine 会运行多个进程&#xff0c;包括 taosd、taosadapter、taoskeeper、taos-explorer 和 taosx。 在这些进程…

OpenCV CUDA模块图像过滤------创建一个盒式滤波器(Box Filter)函数createBoxFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::createBoxFilter 是 OpenCV CUDA 模块中的一个工厂函数&#xff0c;用于创建一个 盒式滤波器&#xff08;Box Filter&#xff09;&…

谷歌I/O 2025 完全指南:由Gemini开创的AI新时代及其对我们未来的影响

在这个朝着智能化一切狂奔的世界中,谷歌I/O 2025不仅展示了人工智能创新——它传递了一个明确的信息:未来已来临,而且由Gemini驱动。 从突破性的模型进步到沉浸式通信工具和个性化AI助手,谷歌正在重塑人机交互的本质。让我们一起了解最重大的公告及其对开发者、用户和AI生…

5000 字总结CSS 中的过渡、动画和变换详解

CSS 中的过渡、动画和变换详解 一、CSS 过渡(Transitions) 1. 基本概念 CSS 过渡是一种平滑改变 CSS 属性值的机制,允许属性值在一定时间内从一个值逐渐变化到另一个值,从而创建流畅的动画效果。过渡只能用于具有中间值的属性(如颜色、大小、位置等),不能用于 displa…

【图像生成大模型】CogVideoX-5b:开启文本到视频生成的新纪元

CogVideoX-5b&#xff1a;开启文本到视频生成的新纪元 项目背景与目标模型架构与技术亮点项目运行方式与执行步骤环境准备模型加载与推理量化推理 执行报错与问题解决内存不足模型加载失败生成质量不佳 相关论文信息总结 在人工智能领域&#xff0c;文本到视频生成技术一直是研…

辨析Spark 运行方式、运行模式(master)、部署方式(deploy-mode)

为了理清 Spark 运行方式、部署模式&#xff08;master&#xff09;、部署方式&#xff08;deploy-mode&#xff09; 之间的关系&#xff0c;我们先明确几个核心概念&#xff0c;再对比它们的联系与区别。 一、核心概念解析 1. Spark 运行方式&#xff08;代码执行方式&#…

从芯片互连到机器人革命:英伟达双线出击,NVLink开放生态+GR00T模型定义AI计算新时代

5月19日&#xff0c;在台湾举办的Computex 2025上&#xff0c;英伟达推出新技术“NVLink Fusion”&#xff0c;允许非英伟达CPU和GPU&#xff0c;同英伟达产品以及高速GPU互连技术NVLink结合使用&#xff0c;加速AI芯片连接。新技术的推出旨在保持英伟达在人工智能开发和计算领…

04算法学习_209.长度最小的子数组

04算法学习_209.长度最小的子数组题目描述&#xff1a;个人代码&#xff1a;学习思路&#xff1a;第一种写法&#xff1a;题解关键点&#xff1a; 第二种写法&#xff1a;题解关键点&#xff1a; 个人学习时疑惑点解答&#xff1a; 04算法学习_209.长度最小的子数组 力扣题目链…

【已解决】docker search --limit 1 centos Error response from daemon

在docker search的时候你是否遇到过这样的问题&#xff1f; Error response from daemon: Get "https://index.docker.io/v1/search?qcentos&n1": dial tcp 103.56.16.112:443: i/o timeout解决方案 可以尝试一下加一层docker镜像代理&#xff1a; 以mysql:5.…

vue好用插件

自动导入插件 cnpm i -D unplugin-auto-import配置 //在vite.config.js文件加入AutoImport({imports:["vue","vue-router","pinia"],dts:true,}),

算法--js--电话号码的字母组合

题&#xff1a;给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 function letterCombinations (digits){if (!digits.length)…