滑动窗口专题:
1. 串联所有单词的⼦串
解题思路:滑动窗口 O(len * N)
研究对象是连续区间,可以尝试用滑动窗口的思想解决。
题目中限定了每个字符串的长度相同,如果把一个个字符串当作字母来处理,就可以转化为「字符串中所有的字⺟异位词」(Leetcode438)。
如果words数组中有n个单词,每个单词长度为len:
- 首先需要用一个哈希表
hash1存储words数组中字符串的种类和个数,另外一个哈希表hash2用于存储滑动窗口中截取子串内部单词的种类和个数。 - 窗口内部,用双指针划分窗口内的每个长度为
len的单词,放入hash2中,再用count来统计窗口中的有效单词。最后通过比对count和n是否相同来更新结果。
那么滑动窗口的起点应该怎么确定呢?通过观察发现,单词的长度 len 是固定统一的,那么仅需从第一个单词的每一位 向后使用一次滑动窗口,就可以把所有情况都包括在内了:

整体思路如下:
由于总共需要遍历len次数组,因此时间复杂度为O(len * N)。
cpp
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words)
{
unordered_map<string, int> hash1; //样板
vector<int> ret;
for(auto& str : words)
{
hash1[str]++;
}
int n = words.size(); //字符串个数
int len = words[0].size(); //字符串单位长度
for(int i=0; i<len; i++)
{
unordered_map<string, int> hash2;
int count = 0;
for(int left=i, right=i; right+len<=s.size(); right+=len)
{
//进窗口+维护count
string in = s.substr(right, len);
hash2[in]++;
if(hash1.count(in) && hash2[in] <= hash1[in])
{
count++;
}
//判断
if(right - left + 1 > len * n)
{
//出窗口+维护count
string out = s.substr(left, len);
//单词存在且单词个数未满 == 有效单词
if(hash1.count(out) && hash2[out] <= hash1[out])
{
count--;
}
hash2[out]--;
left += len;
}
//更新结果
if(count == n)
{
ret.push_back(left);
}
}
}
return ret;
}
};
2. 最⼩覆盖⼦串
解题思路:滑动窗口 O(N)
研究对象是连续区间,可以尝试用滑动窗口的思想解决。
- 使用一个哈希表
hash1统计目标子串中的字符的个数,kinds变量统计hash1中字符的种类。 - 窗口内部,一个哈希表
hash2统计当前窗口中的字符个数。count变量统计hash2中有效字符的种类。最后通过比对count和kinds来更新结果。
一些细节 :由于题目所求的是最小子串,因此可以用substr库函数截取最小子串,这其中需要提供子串的起始下标begin和长度len作为参数,因此需要在更新结果时记录。

两个指针在最坏情况下近似于分别遍历一次子串,相当于N*N,因此时间复杂度为O(N)。
cpp
class Solution {
public:
string minWindow(string s, string t)
{
int hash1[128] = {0};
int kinds = 0;
for(auto ch : t)
{
if(hash1[ch] == 0)
{
kinds++;
}
hash1[ch]++;
}
int begin = -1;
int len = INT_MAX;
int hash2[128] = {0};
for(int left=0, right=0, count=0; right < s.size(); right++)
{
//进窗口
char in = s[right];
hash2[in]++;
if(hash2[in] == hash1[in])
{
count++;
}
//判断
while(count == kinds)
{
//更新结果
if(right - left + 1 < len)
{
begin = left;
len = right - left + 1;
}
//出窗口
char out = s[left];
if(hash2[out] == hash1[out])
{
count--;
}
hash2[out]--;
left++;
}
}
if(begin == -1) return "";
return s.substr(begin, len);
}
};
// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~