滑动窗口_找出字符串中所有字母异位词、串联所有单词的子串_C++
- 1. 题目解析
- 2. 算法分析
- 3. 代码实现
- 4. 举一反三:串联所有单词的子串
1. 题目解析
leetcode链接:https://leetcode.cn/problems/VabMRr/
给定两个字符串 s
和 p
,找到 s
中所有 p
的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
变位词 指字母相同,但排列不同的字符串。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的变位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的变位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的变位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的变位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的变位词。
2. 算法分析
1. 暴力解法
- 首先能判断子串是否是变位词,这可以通过哈希表实现,因为只要是变位词,他们每个字母的数量都是一样的。(以示例1为例)
left
和right
的初始位置如图,之后不断将right
右移,并且将right
指向的元素加入哈希表(记为hash
)。- 当
right
走到e时,发现e不在字符串p
形成的哈希表中(记为hash_p
),需要返回,直接让right
返回,left++
即可。同时right - left = p.size()
,构成了一个变位词。
- 另一种需要返回
right
的情况是,当left
指向第二个b,right
指向第三个b时,此时将right
指向的元素加入hash
,发现hash
中该元素的数量大于hash_p
中该元素的数量,此时也是需要返回的。
2. 滑动窗口
- 在暴力解法的基础上,优化
right
的返回:- 当出现
right
指向的元素不在hash_p
的情况时,直接让left
移动到right
的位置即可,同时清空hash
。 - 当出现
right
指向的元素在hash
中的数量,超过hash_p
时,不需要移动right
,只需要让left
不断++
,然后更改hash
中的元素数量,直到hash
中该元素的数量不再大于hasp_p
即可。
- 当出现
3. 代码实现
1. 暴力解法
- 会超时。
class Solution {
public:
vector<int> findAnagrams(string s, string p)
{
vector<int> ret;
int n = s.size();
int m = p.size();
unordered_map<char, int> hash_p; // 由字符串p形成的哈希表
unordered_map<char, int> hash;
for (auto e : p)
hash_p[e]++;
for (int left = 0, right = 0; right <= n; right++)
{
if (right == n) // 处理边界情况
{
if (right - left == m)
ret.push_back(left);
break;
}
hash[s[right]]++;
if (!hash_p.count(s[right]) || hash[s[right]] > hash_p[s[right]])
{
if (right != n && right - left == m) ret.push_back(left);
right = left;
left++;
hash.clear();
}
}
return ret;
}
};
2. map版
class Solution {
public:
vector<int> findAnagrams(string s, string p)
{
vector<int> ret;
int n = s.size();
int m = p.size();
unordered_map<char, int> hash_p; // 由字符串p形成的哈希表
unordered_map<char, int> hash;
for (auto e : p)
hash_p[e]++;
for (int left = 0, right = 0; right <= n; right++)
{
// 处理边界情况
if (right == n)
{
if (right - left == m)
{
ret.push_back(left);
}
break;
}
hash[s[right]]++; // 进窗口
if (!hash_p.count(s[right]) || hash[s[right]] > hash_p[s[right]])
{
if (right != n && right - left == m) ret.push_back(left); // 更新结果
// 出窗口
if (!hash_p.count(s[right]))
{
left = right + 1;
hash.clear();
}
else if (hash[s[right]] > hash_p[s[right]])
{
while (hash[s[right]] > hash_p[s[right]])
{
hash[s[left]]--;
left++;
if (hash[s[left]] == 0)
hash.erase(s[left]);
}
}
}
}
return ret;
}
};
3. 数组模拟哈希(最快)
class Solution {
public:
vector<int> findAnagrams(string s, string p)
{
vector<int> ret;
int n = s.size();
int m = p.size();
int hash_p[26] = {0};
int hash[26] = {0};
for (auto e : p)
hash_p[e - 'a']++;
for (int left = 0, right = 0; right <= n; right++)
{
// 处理边界情况
if (right == n)
{
if (right - left == m)
{
ret.push_back(left);
}
break;
}
hash[s[right] - 'a']+=1; // 进窗口
if (!hash_p[s[right] - 'a'] || hash[s[right] - 'a'] > hash_p[s[right] - 'a'])
{
if (right != n && right - left == m) ret.push_back(left); // 更新结果
// 出窗口
if (!hash_p[s[right] - 'a'])
{
left = right + 1;
for (int i = 0; i < 26; i++) hash[i] = 0;
}
else if (hash[s[right] - 'a'] > hash_p[s[right] - 'a'])
{
while (hash[s[right] - 'a'] > hash_p[s[right] - 'a'])
{
hash[s[left] - 'a']--;
left++;
}
}
}
}
return ret;
}
};
4. 举一反三:串联所有单词的子串
leetcode链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/description/
class Solution {
public:
string getStr(string s, int pos, int size)
{
string tmp;
for (int i = 0; i < size; i++)
{
tmp += s[pos++];
}
return tmp;
}
vector<int> findSubstring(string s, vector<string>& words)
{
vector<int> ret;
int n = s.size(), m = words.size(), size = words[0].size();
unordered_map<string, int> hash_w;
unordered_map<string, int> hash;
for (auto e : words)
hash_w[e]++;
// 一共滑动size次
for (int i = 0; i < size; i++)
{
hash.clear(); // 细节
for (int left = i, right = i; ; right += size)
{
// 边界情况
if (right + size > n)
{
if ((right - left) / size == m && (right - left) % size == 0)
ret.push_back(left); // 更新结果
break;
}
string tmp = getStr(s, right, size);
hash[tmp]++; // 进窗口
if (!hash_w.count(tmp) || hash[tmp] > hash_w[tmp])
{
if (right != n && (right - left) / size == m && (right - left) % size == 0)
ret.push_back(left); // 更新结果
// 出窗口
if (!hash_w.count(tmp))
{
left = right + size;
hash.clear();
}
else if (hash[tmp] > hash_w[tmp])
{
while (hash[tmp] > hash_w[tmp])
{
string tmp2 = getStr(s, left, size); // 细节
hash[tmp2]--;
left += size;
if (hash.count(tmp2) == 0)
hash.erase(tmp2);
}
}
}
}
}
return ret;
}
};