目录
简介
滑动窗口算法是一种用于解决数组或字符串中的子数组或子串问题的有效算法。它通过使用一个双指针来维护一个滑动窗口来在线性时间内解决问题,通常用于解决一些需要在连续子序列上进行操作的问题,,比如最大值、最小值、平均值、子数组和子串的和等。
思路步骤
这个算法的核心思想是维护一个窗口,该窗口是数组或字符串中的连续部分。随着算法的进行,窗口会在数据结构上进行滑动,从而影响或统计窗口内的元素。
滑动窗口算法的一般步骤:
-
初始化窗口:确定窗口的起始位置和大小。
-
移动窗口:根据特定的条件移动窗口的起始位置和大小。
-
更新窗口:在每次移动窗口时,根据新窗口的情况更新所需的结果。
-
结束条件:根据具体问题的要求,确定算法的结束条件。
滑动窗口算法通常可以在 O(n) 的时间复杂度内解决问题,因为它只对每个元素执行了一次或几次操作,而不是对整个数组执行操作。这使得它在处理大规模数据时具有较高的效率。
我们以一个数组为例,加入当前数组内有7个元素。
最开始时两个指针都在最右侧,一个是左指针,一个是右指针。
随后right根据条件开始移动,假设其根据条件移动了3,当前的窗口大小就是3,从1到3.
随后又因为某些条件使left也移动了1,现在窗口大小就是2
我们可以看到,随着指针的移动,他们之间组成的窗口也在移动,窗口每移动一次,我们就可以得到该窗口现在的信息
当窗口移动到最后时,我们就得到了想要的所有信息。
例题演示
空说概念并不形象,下面我们来看一到利用滑动窗口思想解决的算法题。
LeetCode-2024 考试最大的困扰
https://leetcode.cn/problems/maximize-the-confusion-of-an-exam/description/
题目要求
一位老师正在出一场由 n
道判断题构成的考试,每道题的答案为 true (用 'T'
表示)或者 false (用 'F'
表示)。老师想增加学生对自己做出答案的不确定性,方法是 最大化 有 连续相同 结果的题数。(也就是连续出现 true 或者连续出现 false)。
给你一个字符串 answerKey
,其中 answerKey[i]
是第 i
个问题的正确结果。除此以外,还给你一个整数 k
,表示你能进行以下操作的最多次数:
- 每次操作中,将问题的正确答案改为
'T'
或者'F'
(也就是将answerKey[i]
改为'T'
或者'F'
)。
请你返回在不超过 k
次操作的情况下,最大 连续 'T'
或者 'F'
的数目。
示例 1:
输入:answerKey = "TTFF", k = 2 输出:4 解释:我们可以将两个 'F' 都变为 'T' ,得到 answerKey = "TTTT" 。 总共有四个连续的 'T' 。
示例 2:
输入:answerKey = "TFFT", k = 1 输出:3 解释:我们可以将最前面的 'T' 换成 'F' ,得到 answerKey = "FFFT" 。 或者,我们可以将第二个 'T' 换成 'F' ,得到 answerKey = "TFFF" 。 两种情况下,都有三个连续的 'F' 。
示例 3:
输入:answerKey = "TTFTTFTT", k = 1 输出:5 解释:我们可以将第一个 'F' 换成 'T' ,得到 answerKey = "TTTTTFTT" 。 或者我们可以将第二个 'F' 换成 'T' ,得到 answerKey = "TTFTTTTT" 。 两种情况下,都有五个连续的 'T' 。
题目解析
题目中给了一串答案,并给了k次机会可以修改答案,让我们求出修改答案后最长的连续相同答案。这道题我们就可以利用滑动窗口,可以使用窗口来维护答案。
由于可以修改答案,所以在假设窗口中有m个T,n个F,只要F的数量不超过k,就可以把所有的F全部换成T,同理,只要T的数量不超过k,就可以把所有的T换成F,这样就得到了一串连续相同的答案。随着窗口的移动,我们可以每次记录窗口的长度,窗口在移动时拥有的最大长度,就是T或F被替换后最长的连续答案。
那么这个窗口该怎么移动呢?
移动的条件为,假设我们现在想求替换后可以得到最长T答案串,那么在窗口移动时,窗口内T的数量加上可以替换的数量大于等于窗口的长度时,意味着我们的窗口可以继续向右移动拥有更大的长度,也就是向右移动右指针,因为后面一个字符及时不是T,我们也有足够的机会来替换调新答案。
而当窗口内T的数量加上可以替换的数量小于窗口的长度时,那意味着我们此时只能缩小窗口后才能让窗口继续往右移动,也就是此时我们需要向右移动左指针。
最后当右指针移动到字符串最后时证明移动完成,我们记录下的最长长度就是替换后我们可以得到的最长T字符串。
同理我们再用同样的方法求出最长F字符串,将两次的结果对比,较大的一个就是我们要的结果。
拿示例三举例
字符串为TTFTTFTT,k为1.该字符串我们可以直接看出来,肯定是将F替换为T得到的字符串最长,因此下面我们只讨论求最长T字串的过程。
如果想要找到最开始左右指针都指向第一个字符T
此时窗口长度为1,T的个数为1,1+1>1,因此right向右移动。
当前字符为T
此时窗口长度为2,T的个数为2,2+1>2,因此right向右移动。
当前字符为F
此时窗口长度为3,T的个数为2,2+1=3,因此right还可以继续向右移动。
当前字符为T
此时窗口长度为4,T的个数为3,3+1=4,因此right还可以继续向右移动。
当前字符为T
此时窗口长度为5,T的个数为4,4+1=5,因此right还可以继续向右移动
此时字符为F
此时窗口长度为6,T的个数为4,4+1<6,此时需要将left向右移动,移动后长度为5。后面依次类推,直到left移动到第四个字符T,才再次满足right向右移动的条件。
此时长度为3,后面就可以将right移动到最后了,移动到最后长度也为5.
最后我们就得到了最大的窗口长度——5,也就是我们要的最终答案。
代码实现
代码中使用双层while循环,第一层是向右移动右指针,第二层是当条件不满足时将左指针向右移动,第一层循环的最后会得到当前的最大长度。
class Solution {
public int maxConsecutiveAnswers(String answerKey, int k) {
return Math.max(findMax(answerKey,k,'T'),findMax(answerKey,k,'F'));
}
public int findMax(String answerKey, int k,char c){
int left = 0;
int right = 0;
int maxLen = 0;
int num = 0;
while(right<answerKey.length()){
if(answerKey.charAt(right)==c) num++;
while(num+k<right-left+1){
if(answerKey.charAt(left)==c) num--;
left++;
}
maxLen = Math.max(maxLen,right-left+1);
right++;
}
return maxLen;
}
}