滑动窗口实战:串联所有单词与最小覆盖子串
滑动窗口是处理字符串子串问题的利器。本文通过串联所有单词的子串与最小覆盖子串两道经典题目,演示了如何将字符级窗口扩展至单词级,以及利用哈希表统计频次来判定窗口有效性。
15. 串联所有单词的子串
题目描述
给定一个字符串 s 和一些长度相同的单词 words,找出 s 中恰好由 words 中所有单词串联组成的子串的起始索引。
核心思路
这道题的本质可以看作是在寻找字符串中的「单词异位词」。如果我们将每个单词视为一个整体字符,问题就转化为了在长字符串中寻找特定组合的连续片段。
有几个关键点需要注意:
- 对象粒度:哈希表的键不再是单个字符,而是单词(
string)。 - 步长控制:左右指针的移动步长固定为单词的长度
len。 - 多轮扫描:由于起始位置可能不同,我们需要以
0到len-1为起点分别进行多次滑动窗口遍历。
代码实现
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ret;
unordered_map<string, int> hash1; // 统计目标单词频次
for(auto e : words) {
hash1[e]++;
}
int len = words[0].size();
int m = words.size();
int left = 0;
int right = 0;
// 分 len 次遍历,每次从不同的偏移量开始
for(int i = 0; i < len; i++) {
unordered_map<string, int> hash2; // 当前窗口内的单词统计
int count = 0; // 窗口内有效单词的数量
left = i;
(right = i; right + len <= ()s.(); right += len) {
string str1 = s.(right, len);
hash2[str1]++;
(hash2[str1] <= hash1[str1]) {
count++;
}
(right - left + > len * m) {
string str2 = s.(left, len);
(hash2[str2] <= hash1[str2]) {
count--;
}
hash2[str2]--;
left += len;
}
(count == m) {
ret.(left);
}
}
}
ret;
}
};


