滑动窗口
框架
题型
0003. 无重复字符的最长子串
题目: 给定一个字符串 s
, 找出其中不含有重复字符的最长字串的长度.
示例:
输⼊:s = "abcabcbb"
输出:3
解释:因为⽆重复字符的最⻓⼦串是 "abc",所以其⻓度为 3.
思路:
// 1. 利用双指针构建移动窗口.
// 2. 先右移窗口, window[c]++.
// 3. 当 window[c] > 1 时说明有元素重复了.
// 4. 再左移窗口, window[d]--;
// 5. 记录下当前的 len, 并更新最大 len.
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> window;
int left = 0, right = 0; // 1. 利用双指针构建移动窗口.
int res = 0, n = s.size();
while (right < n) {
char c = s[right++]; // 2. 先右移窗口, window[c]++.
window[c]++;
while (window[c] > 1) {
// 3. 当 window[c] > 1 时说明有元素重复了.
char d = s[left++]; // 4. 这时候左移窗口, window[d]--;
window[d]--;
}
res = max(res, right - left); // 5. 记录下当前的 len, 并更新最大 len.
}
return res;
}
};
0076. 最小覆盖子串
题目: 给定两个字符串 s
和 p
, 返回 s
中涵盖 p
的最小子串.
示例:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
思路:
// 1. 利用双指针构建移动窗口.
// 2. 先右移窗口, 如果当前字符在 p 中且数量相同, window[c]++, valid++.
// 3. 如果当前的 valid 达成了题目的条件, 则可以左移窗口.
// 4. 先更新当前res.
// 5. 再左移窗口, 如果当前字符在 p 中且数量相同, valid--, window[d]--.
代码:
class Solution {
public:
string minWindow(string s, string p) {
unordered_map<char, int> window, need;
for (char c : p) {
need[c]++;}
int left = 0, right = 0; // 1. 利用双指针构建移动窗口.
int valid = 0, n = s.size(), m = p.size();
int start = 0, len = INT_MAX;
while (right < n) {
char c = s[right++]; // 2. 先右移窗口, 如果当前字符在 p 中且数量相同, window[c]++, valid++.
if (need.count(c)) {
window[c]++;
if (window[c] == need[c]) {
valid++;
}
}
while (valid == need.size()) {
// 3. 如果当前的 valid 达成了题目的条件, 则可以左移窗口.
if (right - left < len) {
// 4. 先更新当前res.
start = left;
len = right - left;
}
char d = s[left++]; // 5. 再左移窗口, 如果当前字符在 p 中且数量相同, valid--, window[d]--.
if (need.count(d)) {
if (window[d] == need[d]) {
valid--;
}
window[d]--;
}
}
}
return len == INT_MAX ? "" : s.substr(start, len);
}
};
0239. 滑动窗口的最大值
题目: 给定一个数组 nums
, 有一个长为 k
的滑动窗口从最左侧移动到最右侧, 求滑动窗口中的最大值.
示例:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
思路:
这道题用到了单调队列的方法.
// 1. 定义一个单调队列 q, 先添加 k 个元素于其中, 保存 q.front 为最大值.
// 2. 从 k 处依次添加元素到单调队列中.
// 3. 若最大值索引 <= i - k, 则 q.pop_front, 并保存新的 q.front 为最大值.
代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> q;
int n = nums.size();
vector<int> res;
// 1. 定义一个单调队列 q, 先添加 k 个元素于其中, 保存 q.front 为最大值.
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
res.push_back(nums[q.front()]);
// 2. 从 k 处依次添加元素到单调队列中.
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
// 3. 若最大值索引 <= i - k, 则 q.pop_front, 并保存新的 q.front 为最大值.
if (q.front() <= i - k) {
q.pop_front();
}
res.push_back(nums[q.front()]);
}
return res;
}
};
0438. 找到字符串中的所有异位词 (0567)
题目: 给定两个字符串 s
和 p
, 找出 s
中所有 p
的异位词的字串, 返回子串的起始索引.
示例:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词.
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词.
思路:
// 1. 利用双指针构建移动窗口.
// 2. 先右移窗口, 如果当前字符在 p 中且数量相同, window[c]++, valid++.
// 3. 当窗口宽度 >= p 的宽度, 则该左移窗口.
// 4. 判断有没有达成 valid 的条件, 达成时更新 res.
// 5. 再左移窗口, 如果当前字符在 p 中且数量相同, valid--, window[d]--.
代码
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
unordered_map<char, int> window, need;
for (char c : p) {
need[c]++;}
int left = 0, right = 0; // 1. 利用双指针构建移动窗口.
int valid = 0, n = s.size(), m = p.size();
vector<int> res;
while (right < n) {
char c = s[right++]; // 2. 先右移窗口, 如果当前字符在 p 中且数量相同, window[c]++, valid++.
if (need.count(c)) {
window[c]++;
if (window[c] == need[c]) {
valid++;
}
}
while (right - left >= m) {
// 3. 当窗口宽度 >= p 的宽度, 则该左移窗口.
if (valid == need.size()) {
// 4. 先记录下有没有达成 valid 的条件.
res.push_back(left);
}
char d = s[left++]; // 5. 再左移窗口, 如果当前字符在 p 中且数量相同, valid--, window[d]--.
if (need.count(d)) {
if (window[d] == need[d]) {
valid--;
}
window[d]--;
}
}
}
return res;
}
};