LeetCode---438.找到字符串中所有字母异位词---560.和为k的子数组

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

思路:这道题显然是一道滑动窗口匹配的题目,可以先创建一个pMap用于存储p字符串(原子串)及其对应的字符出现的频率。然后使用sMap维持一个字符个数为p.size()长度的存有s部分子串出现频率映射的哈希表。然后就通过,窗口不断滑动,更新,与比较,如果相同则保存相同位置的起始点进入最终的vector即可。

// @lc code=start
#include<vector>
#include<unordered_map>
#include<string>
using namespace std;

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
            vector<int> ans;
            int pLength = p.size();
            int sLength = s.size();
            if(pLength>sLength) return ans;
            // 使用pMap记录比较窗口
            unordered_map<char,int> pMap;
            for(char c:p){
                pMap[c]++;
            }
            // 使用sMap记录当前的滑动窗口
            unordered_map<char,int> sMap;
            for(int i=0;i<pLength;i++){
                sMap[s[i]]++;
            }
            // 如果第一个滑动窗口就相等则记录
            if(pMap == sMap){
                ans.push_back(0);
            }
            // 开始滑动
            for(int i=pLength;i<sLength;i++){
                sMap[s[i]]++;
                sMap[s[i-pLength]]--;
                if(sMap[s[i-pLength]]==0) sMap.erase(s[i-pLength]);
                if(sMap==pMap){
                    ans.push_back(i-pLength+1);
                }
            }
        return ans;
    }
};

时间复杂度:O(n),n取决于s的长度

空间复杂度:O(m),m取决于p的长度

560.和为k的子数组

思路(错误):考虑使用前缀和以及双指针,分别维持左右两个指针l和r,初始化为0和1,然后维持一个sum初始化为nums[0]+nums[1],做一些初值特判和防止数组越界的操作。在循环中不断比较当前的sum与k的大小关系,如果sum<k,则说明当前l到r范围内的和太小,则是r指针右移(注意越界问题),更新sum。如果sum>k,则说明当前l到r范围内的和太大,则使l指针右移。若相等(比较关键),则是l指针右移,因为我们无法排除当前的nums【l】==0的情况,所以不能让l和r同时右移(最初想法),并且是ans+1。

不过这种思路没过,因为我没有考虑到数组元素是负数的情况。后面加了一些判断还是有问题,就换了第二种思路。

问题代码:

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int l=0,r=1;
        int ans=0;
        if(nums[l]==k) ans++;
        if(nums.size()==1){
            return ans;
        }
        int sum = nums[l]+nums[r];
        while(l<nums.size()){
            if(l==r) sum = nums[l];
            if(sum>k){
                sum -= nums[l];
                l++;
            }else if(sum<k&&nums[l]>0){
                if(r<nums.size()) r++;
                else return ans;
                sum = sum + nums[r];
            }else if(sum<k&&nums[l]<0){
                sum = sum - nums[l];
                l++;
            }
            else{
                ans++;
                l++;
                sum = sum- nums[l];
            }
        }
        return ans;
    }
};

解法2:GPT给出的前缀和+map映射(很牛的方法,可以好好学习)

详细解释

  1. 初始化前缀和

    • currentSum 用于存储当前的前缀和,初始值为 0。
    • 在每次循环中,我们通过 currentSum += num; 更新当前的前缀和。这个操作的意义是将当前元素的值加入到前缀和中。
  2. 前缀和的定义

    • 前缀和是数组中从起始位置到当前位置的所有元素的和。例如,对于数组 [1, 2, 3],前缀和的计算过程如下:
      • 第一个元素:currentSum = 1
      • 第二个元素:currentSum = 1 + 2 = 3
      • 第三个元素:currentSum = 1 + 2 + 3 = 6
  3. 查找条件

    • currentSum - k 的计算是为了找到一个之前的前缀和,使得从这个前缀和到当前元素的子数组的和为 k。
    • 如果 prefixSumCount 中存在 currentSum - k,这意味着从某个位置到当前元素之间的子数组和正好等于 k。
  4. 更新答案

    • ans += prefixSumCount[currentSum - k];:如果找到这样的前缀和,说明有多个子数组的和为 kk。我们将这些子数组的数量加到 ans 中。
    • 这里的 prefixSumCount[currentSum - k] 表示之前有多少次出现过这样的前缀和。
  5. 更新前缀和的出现次数

    • prefixSumCount[currentSum]++;:在每次循环结束时,我们将当前前缀和的出现次数加 1。这是因为当前的 currentSum 现在也可以作为一个前缀和,用于后续的计算。
    • 例如,如果当前 currentSum 是 6,那么在后续的循环中,如果有其他元素使得 currentSum 再次变为 6,我们就能利用之前的出现次数来计算更多的子数组。

整体流程示例

假设我们有一个数组 nums = [1, 1, 1],并且 k=2k=2。

  • 初始状态

    • prefixSumCount 初始化为 {0: 1}(表示前缀和为 0 出现 1 次)。
    • currentSum = 0ans = 0.
  • 遍历过程

    1. 第一次循环(num = 1):

      • currentSum = 0 + 1 = 1
      • 查找 1 - 2 = -1,未找到,ans 仍为 0。
      • 更新 prefixSumCount 为 {0: 1, 1: 1}
    2. 第二次循环(num = 1):

      • currentSum = 1 + 1 = 2
      • 查找 2 - 2 = 0,找到,ans += prefixSumCount[0]ans 变为 1。
      • 更新 prefixSumCount 为 {0: 1, 1: 1, 2: 1}
    3. 第三次循环(num = 1):

      • currentSum = 2 + 1 = 3
      • 查找 3 - 2 = 1,找到,ans += prefixSumCount[1]ans 变为 2。
      • 更新 prefixSumCount 为 {0: 1, 1: 1, 2: 1, 3: 1}
  • 结果

    • 最终 ans 的值为 2,表示数组中有两个子数组和为 kk(即 [1, 1] 和 [1, 1])。

总结

通过前缀和的概念,我们能够在一次遍历中有效地计算出和为 kk 的子数组数量。哈希表 prefixSumCount 用于存储每个前缀和的出现次数,使得我们能够快速查找并更新结果。这个方法的高效性在于它避免了使用双重循环的 O(n2)O(n2) 复杂度,将其降低到 O(n)O(n)。

#include <vector>
#include <unordered_map>

using namespace std;
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> prefixSumCount;
        prefixSumCount[0] = 1; // 处理和为 k 的情况
        int currentSum = 0;
        int ans = 0;

        for (int num : nums) {
            currentSum += num; // 更新当前前缀和

            // 检查是否存在前缀和为 currentSum - k 的情况
            if (prefixSumCount.find(currentSum - k) != prefixSumCount.end()) {
                ans += prefixSumCount[currentSum - k];
            }

            // 更新当前前缀和的计数
            prefixSumCount[currentSum]++;
        }

        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值