滑动窗口是处理字符串和数组问题的经典方案。
窗口滑动的思想:顾名思义,滑动窗口就是滑动的窗口,在字符串上从左往右滑动,直到串尾。滑动窗口的窗口长度是动态变化的,所以用两个指针来维护。
滑动的方向:串首—->串尾。
窗口的大小控制:何时改变i j的值。
窗口滑动实现的数据结构:高效查找,可使用hash。
- 反射弧
1、反转字符串
void reverseString(vector<char>& s) {
int left = 0;
int right = s.size() - 1;
while (left < right) {
swap(s[left], s[right]);
left++;
right--;
}
// reverse(s.begin(), s.end()); // 使用库函数
}
2、反转字符串II
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
note:
其实在遍历字符串的过程中,只要让 i += (2 k),i 每次移动 2 k 就可以了,然后判断是否需要有反转的区间。
string reverseStr(string s, int k) {
for (int i = 0; i < s.size(); i += (2 * k)) {
// 1. 每隔 2k 个字符的前 k 个字符进行反转
// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k <= s.size()) {
reverse(s.begin() + i, s.begin() + i + k );
continue;
}
// 3. 剩余字符少于 k 个,则将剩余字符全部反转。
reverse(s.begin() + i, s.begin() + s.size());
}
return s;
}
3、904水果成篮
// 904.水果成篮
int totalFruit(vector<int>& tree, int K) {
int n = tree.size();
int left = 0;
int right = 0;
vector<int> mp(n, 0);
int ans = INT_MIN;
int count = 0;
while (right < n) {
if (mp[tree[right]] == 0) {
count++;
}
mp[tree[right]]++;
while (count > K) {
mp[tree[left]]--;
if (mp[tree[left]] == 0) {
count--;
}
left++;
}
right++;
ans = max(ans, right - left);
}
return ans;
}
// 795. 区间子数组个数, 注意K的取值
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
return maxK(nums, right) - maxK(nums, left - 1);
}
int maxK(vector<int>& nums, int k) {
int left = 0;
int right = 0;
int ans = 0;
int n = nums.size();
while (right < n) {
if (nums[right] > k) {
right++;
left = right;
continue;
}
right++;
ans += right - left;
}
return ans;
}
// 992. K 个不同整数的子数组
int subarraysWithKDistinct(vector<int>& nums, int k) {
return maxK(nums, k) - maxK(nums, k - 1);
}
int maxK(vector<int>& nums, int k)
{
vector<int> mp(nums.size() + 1, 0);
int left = 0;
int right = 0;
int count = 0;
int res = 0;
while (right < nums.size()) {
if (mp[nums[right]] == 0) {
count++;
}
mp[nums[right]]++;
right++;
while (count > k) {
mp[nums[left]]--;
if (mp[nums[left]] == 0) {
count--;
}
left++;
}
res += right - left; // 为什么用子数组的长度即right - left来表示增加的子数组个数呢?
}
return res;
}
/*
为什么可以新用子数组的长度即right - left来表示增加的子数组个数呢?
可以借鉴动态规划的思想,举个例子就好理解了:
当满足条件的子数组从[A,B,C]增加到[A,B,C,D]时,新子数组的长度为4,同时增加的子数组为[D],[C,D],[B,C,D],[A,B,C,D]也为4。
*/