力扣日记:【哈希表篇】有效的字母异位词 相关推荐
日期:2023.8.29
参考:代码随想录、力扣
438.找到字符串中所有字母异位词
题目描述
难度:中等
给定两个字符串 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" 的异位词。
提示:
- 1 <= s.length, p.length <= 3 * 10^4
- s 和 p 仅包含小写字母
题解
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
// 时间复杂度:O(m)+O(m)+O(n-m)=O(n+m)
// 空间复杂度:O(∑)
vector<int> answer;
unordered_map<char, int> record;
unordered_map<char, int> for_check;
int sLen = s.size();
int pLen = p.size();
for (const auto& ch: p) {
record[ch]++;
for_check[ch]++;
}
int front = 0, end = 0;
while(end < sLen) {
while (end - front < pLen) { // 将end移到对应位置(初始化滑动窗口)
if (for_check.find(s[end]) != for_check.end()) { // 该字符存在于p中
record[s[end]]--;
if (record[s[end]] == 0) {
record.erase(s[end]); // 通过键删除对应键值对
}
}
end++;
}
// 判断是否是异位词
if (record.size() == 0) {
answer.push_back(front);
}
if (for_check.find(s[end]) != for_check.end()) { // 该字符存在于p中
record[s[end]]--;
if (record[s[end]] == 0) {
record.erase(s[end]); // 通过键删除对应键值对
}
}
if (for_check.find(s[front]) != for_check.end()) {
record[s[front]]++;
if (record[s[front]] == 0) {
record.erase(s[front]);
}
}
front++;
end++;
}
// 判断是否是异位词
if (record.size() == 0) { // 最后还要检查一次
answer.push_back(front);
}
return answer;
}
};
复杂度
- 哈希表比较+哈希表差值
时间复杂度::初始化
record和for_check需要O(2m)=O(m),初始化滑动窗口O(m),滑动及判断需要O(3(n-m))=O(n-m),因此需要O(n+m)
空间复杂度:O(2∑)=O(∑) // ∑为p中字母种类数(需要两个哈希表)
思路总结
- 使用哈希表虽然复杂度看上去低,但实际执行时间和占用内存会比较大
- 总体思路是:与p长度相同的滑动窗口依次向右移动,每次判断滑动窗口是否是异位词,是则记录滑动窗口的起始位置。
- 为了利用哈希表查询快的特性,使用哈希表
for_check和record分别记录p以及滑动窗口中字母种类和对应个数。 - 其中记录p的哈希表即
for_check,用于判断是否滑动窗口起始或终止位置的字符是否存在于p中,如
只有该字符存在于p中,才进行哈希表for_check.find(s[end]) != for_check.end() 或 for_check.find(s[front]) != for_check.end()record的更新(for_check在滑动过程中不更新,只用于判断)。 - 而哈希表
record用于判断滑动窗口中的字符是否是p的异位词,其表示的实际上是两者的差值,因此当窗口终止位置(即下一个即将包含进窗口的字符)存在于p中,则record对应字符个数–(差距变小了),而当窗口起始位置(即下一个即将退出窗口的字符)存在于p中,则对应字符个数++(差距变大了)。当record中各个字符个数均为0,则表示为异位词。 - 更进一步的,为了使得判断是否异位词的复杂度为O(1),使用
record.size() == 0进行判断。因此,在每次滑动窗口++或–后若对应字符个数为0,需要进行erase操作。 - 力扣官方题解 使用了数组进行判断。
本文介绍了如何使用哈希表和滑动窗口技巧解决LeetCode上的438题,通过对比字符串s和目标字符串p中字符的出现次数来查找所有异位词的子串起始索引。

被折叠的 条评论
为什么被折叠?



