滑动窗口
约 1155 字大约 4 分钟
模板
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动
窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?
解题的关键在于 窗口的起始位置如何移动
//外层循环扩展右边界,内层循环扩展左边界
for (int l = 0, r = 0 ; r < n ; r++) {
//当前考虑的元素
while (l <= r && check()) {//区间[left,right]不符合题意
//扩展左边界
}
//区间[left,right]符合题意,统计相关信息
}
3无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
时间复杂度:O(N),其中 N 是字符串的长度。左指针和右指针分别会遍历整个字符串一次。
空间复杂度:O(∣Σ∣),其中 Σ 表示字符集(即字符串中可以出现的字符),∣Σ∣ 表示字符集的大小。在本题中没有明确说明字符集,因此可以默认为所有 ASCII 码在 [0,128) 内的字符,即 ∣Σ∣=128。我们需要用到哈希集合来存储出现过的字符,而字符最多有 ∣Σ∣ 个,因此空间复杂度为 O(∣Σ∣)。
class Solution {
public int lengthOfLongestSubstring(String s) {
//滑动窗口
char[] ss = s.toCharArray();
Set<Character> set = new HashSet<>();//去重
int res = 0;//结果
for(int left = 0, right = 0; right < s.length(); right++) {//每一轮右端点都扩一个。
char ch = ss[right];//right指向的元素,也是当前要考虑的元素
while(set.contains(ch)) {//set中有ch,则缩短左边界,同时从set集合出元素
set.remove(ss[left]);
left++;
}
set.add(ss[right]);//别忘。将当前元素加入。
res = Math.max(res, right - left + 1);//计算当前不重复子串的长度。
}
return res;
}
}
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" 的异位词。
时间复杂度:O(m+(n−m)×Σ),其中 n 为字符串 s 的长度,m 为字符串 p 的长度,Σ 为所有可能的字符数。我们需要 O(m) 来统计字符串 p 中每种字母的数量;需要 O(m) 来初始化滑动窗口;需要判断 n−m+1 个滑动窗口中每种字母的数量是否与字符串 p 中每种字母的数量相同,每次判断需要 O(Σ) 。因为 s 和 p 仅包含小写字母,所以 Σ=26。
空间复杂度:O(Σ)。用于存储字符串 p 和滑动窗口中每种字母的数量。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<Integer>();
}
List<Integer> ans = new ArrayList<Integer>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < pLen; ++i) {
++sCount[s.charAt(i) - 'a'];
++pCount[p.charAt(i) - 'a'];//记录26字母在s和p中出现的次数,匹配词
}
if (Arrays.equals(sCount, pCount)) {
ans.add(0);
}
for (int i = 0; i < sLen - pLen; ++i) {
--sCount[s.charAt(i) - 'a'];//减去上一个字符在26字母中出现的次数
++sCount[s.charAt(i + pLen) - 'a'];//增加本次字符在26字母中出现的次数
if (Arrays.equals(sCount, pCount)) {
ans.add(i + 1);
}
}
return ans;
}
}