滑动窗口题目整理
通过 LeetCode 上的 5 道简单的题总结如下:
无论是窗口可变还是窗口恒定,用两个指针 left 和 right 来维护一个窗口,要考虑的是窗口何时满足条件,要处理的就是元素进窗口以及元素离开窗口这两个状态。
无重复字符的最长子串
题意: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
满足条件的状态: 窗口中的字符没有重复
不满足条件的状态: 窗口中的字符有重复,此时需要移动 left 指针来使窗口重新满足条件。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int front = 0, right = 0;
int len = s.length();
int ans = 0;
int a[1000]={0};
while (right < len) {
a[s[right]]++;
if (a[s[right]] == 1) {
ans = max(ans, right - front + 1);
}else if (a[s[right]] == 2) {
while (s[right] != s[front]) {
a[s[front]]--;
front++;
}
a[s[front]]--;
front++;
}
right++;
}
return ans;
}
};
串联所有单词的子串
题意: 给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。(注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。)
题解: 由单词的长度相同出发,可以将 s 按照单词的长度切割成若干个单词序列。从 s 的第一个单词开始切割与从 s 的第二个单词开始切割,所得到的单词序列不同。为了得到所有的情况,需要从 s 的前 words 长度出发都切割一遍。
满足条件的状态: 窗口中恰好包含所有单词 words。
不满足条件的状态: 情况1:窗口中某个单词多了,移动 left 指针,使该单词个数满足条件
情况2:窗口中出现了其他单词,删除当前窗口中所有单词,使窗口大小为 0,重新开始匹配
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> wordhash;
int num = words.size();
for (int i = 0; i < num; ++i) {
wordhash[words[i]]++;
}
int len = words[0].length();
int windowlen = len * num;
int n = s.length();
vector<int> ans;
for (int i = 0; i < len; ++i) {
unordered_map<string, int> nowhash;
int left = i, right = i;
while (n - right >= len) {
string temp = s.substr(right, len);
right += len;
//新单词不合法时
if (wordhash.find(temp) == wordhash.end()) {
left = right;
nowhash.clear();
}else if (nowhash[temp] == wordhash[temp]) {
while (left < right) {
string t = s.substr(left, len);
left += len;
if (t == temp) {
break;
}
nowhash[t]--;
}
}else {//合法
nowhash[temp]++;
}
if (right - left == windowlen) {//判断是否单词完全匹配
ans.push_back(left);
}
}
}
return ans;
}
};
最小覆盖子串
题意: 给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
满足条件的状态: 窗口中至少包含 T 的所有字符。此时,尽可能移动 left 指针,使得字符串长度尽可能小。
class Solution {
public:
bool check(unordered_map<char, int> &a, unordered_map<char, int> &b) {
unordered_map<char, int>::iterator it;
for (it = a.begin(); it != a.end(); ++it) {
if (a[it->first] > b[it->first])
return false;
}
return true;
}
string minWindow(string s, string t) {
unordered_map<char, int> st;
int len = t.length();
for (int i = 0; i < len; ++i) {
st[t[i]]++;
}
int n = s.length();
int left = 0, right = 0;
int ans = n+1, pos = 0;
unordered_map<char, int> nows;
while (right < n) {
char temp = s[right];
right++;
nows[temp]++;
while (check(st, nows) && left < right) {//满足包含所有这一条件时
if (right - left < ans) {
ans = right - left;
pos = left;
}
nows[s[left]]--;
left++;
}
}
if (ans == n+1) ans = 0;
return s.substr(pos, ans);
}
};
字符串的排列
题意: 给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
题解: 这题其实是第二题的简单版。
class Solution {
public:
bool check(unordered_map<char, int> &a, unordered_map<char, int> &b) {
unordered_map<char, int>::iterator it;
for (it = a.begin(); it != a.end(); ++it) {
if (it->second != b[it->first])
return false;
}
return true;
}
bool checkInclusion(string s1, string s2) {
int len = s1.length(), n = s2.length();
unordered_map<char, int> cmp;
unordered_map<char, int> now;
for (int i = 0; i < len; ++i) {
cmp[s1[i]]++;
}
int left = 0, right = 0;
while (right < n) {
char t = s2[right];
right++;
now[t]++;
if (right - left == len) {
if (check(cmp, now)) {
return true;
}
now[s2[left]]--;
left++;
}
}
return false;
}
};
滑动窗口的最大值
题意: 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
题解: 这个题与上面几个题不同之处在于滑动窗口大小固定,需要一个贪心的思想,即每次进窗口的值需要把之前的比它小的数全部去除,因为它们将不会再是最大值了。需要用一个双端队列来维护这个最大值。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans;
deque<int> wd;
int n = nums.size();
for (int i = 0; i < k; ++i) {
while (!wd.empty() && nums[i] > nums[wd.back()]) {
wd.pop_back();
}
wd.push_back(i);
}
ans.push_back(nums[wd.front()]);
for (int i = k; i < n; ++i) {
if (i - wd.front() >= k) {
wd.pop_front();
}
while (!wd.empty() && nums[i] > nums[wd.back()]) {
wd.pop_back();
}
wd.push_back(i);
ans.push_back(nums[wd.front()]);
}
return ans;
}
};