Problem: 438. 找到字符串中所有字母异位词 ps:record数组也就是官方题解中的count,differNum也就是官方题解中的differ
record[i]的含义
record[i] > 0:比较s上的滑动窗口和p,对于对应的字母的数量,s上的滑动窗口比p多
record[i] = 0:比较s上的滑动窗口和p,对于对应的字母的数量,s上的滑动窗口和p一样多
record[i] < 0:比较s上的滑动窗口和p,对于对应的字母的数量,s上的滑动窗口比p少
differNum的含义
比较s上的滑动窗口和p,record数组(也就是26个小写字母)中不同的字母个数(也就是record数组中值不等于0的元素个数)
注意!2024年3月22日二刷之后的更正:differNum的含义应该是record数组中不为0的元素的个数,而不是s上的滑动窗口和p中不同字母的个数!两者是不一样的,比如滑动窗口为"bae",p为"abc",differNum的个数应该是2,但是两者相比不同的字母数为1。所以diffNum的正确含义应该是s上的滑动窗口与p中不同字母数的两倍!
关键代码段
如果滑动窗口最左边的字母在record中原来是1(说明滑动窗口比p多),那么减去它后变成0(说明滑动窗口和p相同),那么differNum要减一(说明不同的字母少了一个)。
如果滑动窗口最左边的字母在record中原来是0(说明滑动窗口和p相同),那么减去它后变成-1(说明滑动窗口比p少),那么differNum要加一(说明不同的字母多了一个)。
如果滑动窗口最左边的字母在record中原来是-1(说明滑动窗口比p少),那么减去它后变成-2(说明滑动窗口更少),那么differNum不变(说明不同的字母个数不变)。【官方题解中省略了这一步,增添了理解难度】
如果滑动窗口最右边的字母在record中原来是-1(说明滑动窗口比p少),那么加上它后变成0(说明滑动窗口和p相同),那么differNum要减一(说明不同的字母少了一个)。
如果滑动窗口最右边的字母在record中原来是0(说明滑动窗口和p相同),那么加上它后变成1(说明滑动窗口比p多),那么differNum要加一(说明不同的字母多了一个)。
如果滑动窗口最右边的字母在record中原来是1(说明滑动窗口比p多),那么加上它后变成2(说明滑动窗口更多),那么differNum不变(说明不同的字母个数不变)。【官方题解中省略了这一步,增添了理解难度】
for (int i = 0;i < sLen - pLen; i++) {
if (record[s[i] - 'a'] == 1) { // 对于s[i]对应字母的个数,滑动窗口比p多
differNum--; // 减去之后变成0,所以differNum减少
} else if (record[s[i] - 'a'] == 0) { // 对于s[i]对应字母的个数,滑动窗口和p一样多
differNum++; // 减去之后变成-1,所以differNum增加
} else { // 对于s[i]对应字母的个数,滑动窗口比p少 (也可以理解成小于等于-1的情况)
// 减去之后小于等于-2,没变化,所以do nothing
}
record[s[i] - 'a']--;
if (record[s[i + pLen] - 'a'] == -1) { // 对于s[i + pLen]对应字母的个数,滑动窗口比p少
differNum--; // 加上之后变成0,所以differNum减少
} else if (record[s[i + pLen] - 'a'] == 0) { //对于s[i + pLen]对应字母的个数,滑动窗口和p相同
differNum++; // 加上之后变成1,所以differNum增加
} else { // 对于s[i + pLen]对应字母的个数,滑动窗口比p多(也可以理解成大于等于-1的情况)
// 加上之后大于等于2,没变化,所以do nothing
}
record[s[i + pLen] - 'a']++;
if (differNum == 0) {
ans.emplace_back(i + 1);
}
}
复杂度
- 时间复杂度:
- 空间复杂度:
Code
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sLen = s.size();
int pLen = p.size();
vector<int> ans;
int differNum = 0; // 仅引入一个int型的differNum不能实现,必须再引入一个数组
int record[26] = {0}; // 列表初始化为0
// 特殊情况
if (sLen < pLen) {
return ans;
}
// 滑动窗口初始情况
for (int i = 0; i < pLen; i++) {
record[s[i] - 'a']++;
record[p[i] - 'a']--;
}
// record[i]为负数,表示:对于对应的字母,比较p和s上的滑动窗口,p中更多,多record[i]的绝对值;record[i]为正数,表示:对于对应的字母,比较p和s上的滑动窗口,s上的滑动窗口更多,多是record[i];若record[i]为0,相同。
for (int i = 0; i < 26; i++) {
if (record[i] != 0) { // 为什么是!= ??? 结合differNum的含义理解
differNum++;
}
}
if (differNum == 0) {
ans.emplace_back(0); // 优化版的push_back();
}
// 滑动窗口移动
for (int i = 0;i < sLen - pLen; i++) {
if (record[s[i] - 'a'] == 1) { // 对于s[i]对应字母的个数,滑动窗口比p多
differNum--; // 减去之后变成0,所以differNum减少
} else if (record[s[i] - 'a'] == 0) { // 对于s[i]对应字母的个数,滑动窗口和p一样多
differNum++; // 减去之后变成-1,所以differNum增加
} else { // 对于s[i]对应字母的个数,滑动窗口比p少 (也可以理解成小于等于-1的情况)
// 减去之后小于等于-2,没变化,所以do nothing
}
record[s[i] - 'a']--;
if (record[s[i + pLen] - 'a'] == -1) { // 对于s[i + pLen]对应字母的个数,滑动窗口比p少
differNum--; // 加上之后变成0,所以differNum减少
} else if (record[s[i + pLen] - 'a'] == 0) { //对于s[i + pLen]对应字母的个数,滑动窗口和p相同
differNum++; // 加上之后变成1,所以differNum增加
} else { // 对于s[i + pLen]对应字母的个数,滑动窗口比p多(也可以理解成大于等于-1的情况)
// 加上之后大于等于2,没变化,所以do nothing
}
record[s[i + pLen] - 'a']++;
if (differNum == 0) {
ans.emplace_back(i + 1);
}
}
return ans;
}
};