leetcode hot100 #9 滑动窗口 #438 找到字符串中所有字母异位词

找到字符串中所有字母异位词

题目

给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

题解

定长滑动窗口

记录字符数量

算法
对长度为pLen的字符串p,维护一个长度为pLen的窗口,在vector<int> pCount中记录窗口中字符数量
然后对字符串s的前pLen位,同样维护一个长度为pLen的窗口,在sCount中记录窗口中字符数量
比较pCountsCount,若相等,则记录位置0。
然后开始滑动s上的窗口,在sCount中减去出去字符的数量,加上进来字符的数量
在每个位置pCountsCount,并记录相等时的位置。
代码

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int sLen = s.size(), pLen = p.size();

        if (sLen < pLen) {
            return vector<int>();
        }

        vector<int> ans;
        vector<int> sCount(26);
        vector<int> pCount(26);
        for (int i = 0; i < pLen; ++i) {
            ++sCount[s[i] - 'a'];
            ++pCount[p[i] - 'a'];
        }

        if (sCount == pCount) {
            ans.emplace_back(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            --sCount[s[i] - 'a'];
            ++sCount[s[i + pLen] - 'a'];

            if (sCount == pCount) {
                ans.emplace_back(i + 1);
            }
        }

        return ans;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度

  • 时间复杂度:O(m+(nm)×Σ),其中 n 为字符串 s 的长度,m 为字符串p 的长度,Σ 为所有可能的字符数。因为 sp 仅包含小写字母,所以 Σ=26。
  • 空间复杂度:O(Σ)。用于存储字符串 p 和滑动窗口中每种字母的数量。

记录字符差

算法
维护一个大小为26的vector<int> count来记录sp中的26种字符数量之差,维护一个diff表示sp中 字符数不同 的 字符 的种类。
统计s中字符数量,减去p中字符数量,得到count,其中大于0的元素表示窗口中s中对应字符比p多的数量,小于0的元素表示窗口中s中对应字符比p少的数量

在每个位置,对于即将窗口的字符c,若countc对应值为1,说明,窗口中s中对应字符只比p多1,当c出窗口,spc字符数将相等,diff减一。
countc对应值为0,说明,窗口中sp中字符c数量相等,当c出窗口,spc字符数不同的字符的种类将增加,diff加一。

在每个位置,对于即将窗口的字符c,若countc对应值为-1,说明,窗口中s中对应字符只比p少1,当c进窗口,spc字符数将相等,diff减一。
countc对应值为0,说明,窗口中sp中字符c数量相等,当c出窗口,spc字符数不同的字符的种类将增加,diff加一。
代码

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int sLen = s.size(), pLen = p.size();

        if (sLen < pLen) {
            return vector<int>();
        }

        vector<int> ans;
        vector<int> count(26);
        for (int i = 0; i < pLen; ++i) {
            ++count[s[i] - 'a'];
            --count[p[i] - 'a'];
        }

        int differ = 0;
        for (int j = 0; j < 26; ++j) {
            if (count[j] != 0) {
                ++differ;
            }
        }

        if (differ == 0) {
            ans.emplace_back(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            if (count[s[i] - 'a'] == 1) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
                --differ;
            } else if (count[s[i] - 'a'] == 0) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
                ++differ;
            }
            --count[s[i] - 'a'];

            if (count[s[i + pLen] - 'a'] == -1) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
                --differ;
            } else if (count[s[i + pLen] - 'a'] == 0) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
                ++differ;
            }
            ++count[s[i + pLen] - 'a'];
            
            if (differ == 0) {
                ans.emplace_back(i + 1);
            }
        }

        return ans;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度

  • 时间复杂度:O(n+m+Σ),其中 n 为字符串 s 的长度,m 为字符串p 的长度,Σ 为所有可能的字符数。因为 sp 仅包含小写字母,所以 Σ=26。
  • 空间复杂度:O(Σ)。用于存储字符串 p 和滑动窗口中每种字母的数量。

变长滑动窗口

算法
维护一个count来记录p中各字符与s窗口中字符之差,
初始窗口长度为0,因此各count各元素值等于p中各字符数量。
每次循环,窗口边界向滑动 1 单位。
当窗口右侧进入新字符ccount中对应元素值减 1。若count的对应元素值count[c]<0,说明窗口中字符c数量比p中字符c数量多,此时,窗口左边界向右收缩,出去的字符在count中对应元素值加 1,收缩直到countc对应值count[c]=0,也就是最后窗口中出去了一个字符c
此时,计算窗口长度,若等于p长度,说明窗口中字符与p中字符相同,记录位置。

分析可得,窗口大小一定小于等于字符串p的大小。对于每种字符c,窗口中该字符c的数量一定小于等于p,当且仅当窗口长度等于p长度时,窗口中字符与p中字符相同。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int n = s.size(), m = p.size();
        if(n < m) return {};
        vector<int> count(26);
        for(auto ch : p) ++count[ch - 'a'];
        vector<int> ret;
        for(int l = 0, r = 0; r < n; ++r) {
            --count[s[r] - 'a'];
            while(count[s[r] - 'a'] < 0) {
                ++count[s[l] - 'a'];
                ++l;
            }
            if(r - l + 1 == m) ret.push_back(l);
        }
        return ret;
    }
};
//https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/comments/1291207

复杂度

  • 时间复杂度:O(n+m),其中 n 为字符串 s 的长度,m 为字符串p 的长度。看似是for循环中套了一个while循环,实际指针l和指针r只向右移动,最大移动距离为n
  • 空间复杂度:O(Σ)。Σ 为所有可能的字符数。因为 sp 仅包含小写字母,所以 Σ=26。
  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值