找到字符串中的所有字母异位词——力扣官方题解方法二解析

119 阅读5分钟

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);
    }
}

复杂度

  • 时间复杂度:

O(pLen+sLen)O(pLen + sLen)

  • 空间复杂度:

O(1)O(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;
    }
};