题目
思路
多起点的滑动窗口
- 注意到单词为无序串联,所以所需要的查找与单词顺序无关,只与单词的数量有关。
- 用哈希表 vm 存储 words 中每个单词出现的频次。
- 在串 s 中运用滑动窗口,每次滑动的长度为一个单词的长度 d,同时用哈希表记录窗口中单词出现的频次。
- 随着窗口的滑动,判断 vm 与此时窗口的哈希表是否相等,若相等,则保存此时的结果。
- 初始化 d 个滑动窗口,即 d 个起点的滑动窗口,可以保证不遗漏。
代码实现
class Solution {
public:
std::vector<int> findSubstring(std::string s, std::vector<std::string>& words) {
int length = s.length(); // 总串的长度
int d = words[0].length(); // 每个单词的长度
int m = words.size(); // 要查找的单词个数
int l = d * m; // 要查找的串的长度
std::vector<int> res; // 保存所有结果
std::unordered_map<std::string, int> um; // 存放words中每个单词的词频
for (const auto& word : words)
{
um[word]++;
}
// 初始化 d 个滑动窗口
std::vector<std::unordered_map<std::string, int>> v_um(d);
for (int i = 0; i < d && i <= length - l; i++) // length - l 为 i 最后一个可能的位置,若超过,则后面的串必然不可能有需要查找的串的长度
{
for (int j = i; j < i + l; j += d) // i 到 i + l 是窗口的大小
{
std::string str = s.substr(j, d);
v_um[i][str]++; // 哈希表记录此时创建的单词频次
}
if (v_um[i] == um) // 判断窗口内的单词词频是否和目标词频相等
res.push_back(i);
}
// 滑动窗口
for (int i = d; i <= length - l; i++) // 第一个窗口已经判断过,从第二个窗口开始判断
{
int r = i % d;
std::string wl = s.substr(i - d, d); // 左侧的旧单词
std::string wr = s.substr(i + l - d, d); // 右侧的新单词
if (--v_um[r][wl] == 0) // 更新窗口后需要更新相应的哈希表,如果单词频次降为0,则从哈希表中删除该键
v_um[r].erase(wl);
v_um[r][wr]++; // 将右侧单词加入哈希表
// 判断窗口内的单词词频是否和目标词频相等
if (v_um[r] == um)
res.push_back(i);
}
return res;
}
};