滑动窗口算法(Sliding Window Algorithm)是一种用于解决数组或字符串相关问题的常用技术。它通过维护一个固定大小的窗口,并在窗口内移动来处理数据。该算法通常用于在线性时间内解决连续子数组或子字符串的问题。
滑动窗口算法的基本思想是通过调整窗口的起始位置和结束位置,以有效地处理数据。算法的核心步骤如下:
1、初始化窗口的起始位置和结束位置,通常为数组或字符串的开头。
2、在每次迭代中,计算当前窗口的状态,并处理相关数据。
3、根据问题的要求,调整窗口的起始位置和结束位置,使窗口滑动到下一个位置。
4、重复步骤2和步骤3,直到窗口滑动到数据的末尾。
滑动窗口算法的关键是确定窗口的大小和如何移动窗口。这取决于具体的问题。下面是两个示例来说明滑动窗口算法的应用:
1、最大子数组和问题(Maximum Subarray Sum Problem):给定一个整数数组,找到该数组中和最大的连续子数组。滑动窗口算法可以通过保持一个窗口的和,将窗口向右移动一位,并根据需要调整窗口的大小来解决这个问题。
#include <iostream>
#include <vector>
int maxSubarraySum(const std::vector<int>& nums, int k) {
int n = nums.size();
int windowSum = 0;
int maxSum = INT_MIN;
// 计算初始窗口的和
for (int i = 0; i < k; ++i) {
windowSum += nums[i];
}
// 滑动窗口
for (int i = k; i < n; ++i) {
windowSum += nums[i] - nums[i - k]; // 加上新元素,减去窗口的最左侧元素
maxSum = std::max(maxSum, windowSum);
}
return maxSum;
}
int main() {
std::vector<int> nums = {1, -3, 2, 4, -1, 5, -7, 2, 6};
int k = 3; // 窗口大小
int result = maxSubarraySum(nums, k);
std::cout << "Maximum subarray sum: " << result << std::endl;
return 0;
}
运行上述代码将输出最小覆盖子串为"11"。
2、字符串最小覆盖子串问题(Minimum Window Substring Problem):给定一个源字符串和一个目标字符串,在源字符串中找到包含目标字符串所有字符的最短子串。滑动窗口算法可以通过维护两个指针,一个指向窗口的起始位置,一个指向窗口的结束位置,并根据目标字符串的出现情况来移动窗口,从而找到最小的覆盖子串。
#include <iostream>
#include <unordered_map>
std::string minWindowSubstring(const std::string& source, const std::string& target) {
std::unordered_map<char, int> targetMap;
std::unordered_map<char, int> windowMap;
int left = 0;
int right = 0;
int minLen = INT_MAX;
int minStart = 0;
int count = 0;
// 统计目标字符串中每个字符的出现次数
for (char ch : target) {
++targetMap[ch];
}
// 滑动窗口
while (right < source.length()) {
char ch = source[right];
++windowMap[ch];
// 如果当前字符在目标字符串中,并且窗口中该字符的出现次数不超过目标字符串中该字符的出现次数,则count加1
if (targetMap.count(ch) && windowMap[ch] <= targetMap[ch]) {
++count;
}
// 如果窗口包含目标字符串中的所有字符
while (count == target.length()) {
// 更新最小覆盖子串的起始位置和长度
if (right - left + 1 < minLen) {
minLen = right - left + 1;
minStart = left;
}
// 缩小窗口,尝试去掉最左侧的字符
char leftCh = source[left];
--windowMap[leftCh];
// 如果移除最左侧字符后,窗口不再包含目标字符串中的所有字符,则count减1
if (targetMap.count(leftCh) && windowMap[leftCh] < targetMap[leftCh]) {
--count;
}
++left;
}
++right;
}
if (minLen == INT_MAX) {
return "";
}
return source.substr(minStart, minLen);
}
int main() {
std::string source = "ADOBECODEBANC";
std::string target = "ABC";
std::string result = minWindowSubstring(source, target);
std::cout << "Minimum window substring: " << result << std::endl;
return 0;
}
运行上述代码将输出最小覆盖子串为"BANC"。