滑动窗口算法是一种解决数组或字符串中连续子数组或子字符串问题的有效技巧。该算法通过维护一个窗口,该窗口在数组或字符串上滑动,以找到符合特定条件的子数组或子字符串。
通常,滑动窗口算法可以用于解决以下类型的问题:
- 找到具有特定性质的子数组或子字符串,例如最小值、最大值、总和等。
- 查找包含给定元素的子数组或子字符串。
- 找到满足一定条件的最短子数组或子字符串。
滑动窗口算法的一般步骤如下:
- 初始化窗口的起始位置和大小。
- 将窗口的右边界向右移动,直到满足特定条件。
- 缩小窗口大小或移动左边界,以满足问题的要求。
- 重复步骤2和3,直到遍历完整个数组或字符串。
这种算法的关键在于如何有效地移动窗口,并在每个步骤中更新窗口内的状态,以在满足特定条件时找到所需的结果。这种算法通常具有较低的时间复杂度,因为在每个步骤中,只需要对窗口中的元素进行常数级别的操作。
举例来说,如果要解决一个找到数组中和大于等于某个特定值的最短子数组的问题,可以使用滑动窗口算法。在这种情况下,我们可以通过不断地增加窗口的右边界,并根据当前窗口内的和来移动左边界,以找到最短的满足条件的子数组。
下面举个例题:
假设我们选择字符串中的第 k 个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 rk 。那么当我们选择第 k+1字符作为起始位置时,首先从 k+1到 rk的字符显然是不重复的,并且由于少了原本的第 k个字符,我们可以尝试继续增大 rk,直到右侧出现了重复字符为止。
这样一来,我们就可以使用「滑动窗口」来解决这个问题了:
- 我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的 rk
- 在每一步的操作中,我们会将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置 ,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始 的,不包含重复字符的最长子串。我们记录下这个子串的长度;
- 在枚举结束后,我们找到的最长的子串的长度即为答案
java
class Solution {
public int lengthOfLongestSubstring(String s) {
//哈希集合,记录每个字符是否出现过
Set<Character> occ = new HashSet<Character>();
int n=s.length();
//右指针,初始值为-1;相当于我们在字符串的左边界的左侧,还没有开始移动
int rk=-1,ans=0;
for (int i = 0; i < n; i++) {
if(i!=0){
//左指针向右移动一格,移除一个字符
occ.remove(s.charAt(i-1));
}
while(rk + 1 < n && !occ.contains(s.charAt(rk + 1))){
//不断向右移动指针
occ.add(s.charAt(rk+1));
++rk;
}
//第i到rk个字符是一个极长的无重复字符字串
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}