【前缀和:3. 无重复字符的最长子串】

题目链接:3. 无重复字符的最长子串

连续子数组问题,可以使用前缀和

而使用滑动窗口必须满足单调性!

方法1:前缀和+暴力枚举
  1. 统计前缀和
  2. 枚举前缀和数组的右端点,然后对于每个右端点,每次都枚举所有端点
java 复制代码
class Solution {
  public int subarraySum(int[] nums, int k) {
    int n = nums.length;
    int [] s = new int[n + 1];
    //统计前缀和
    for (int i = 0; i < n; i++){
      s[i+1] = s[i] + nums[i];
    }
    int ans = 0;
    for(int right = 1; right < n+1; right++){
      int left = 0;
      //暴力枚举
      while (left < right){
        if (s[right] - s[left] == k){
          ans++;
        }
        left++;
      }

    }
    return ans;
  }
}
方法2:前缀和+哈希表
  1. 我们统计完前缀和之后,我们要找的是s[j]-s[i]==k
  2. 可以把问题转换成s 中有多少对下标 (i,j) 满足 0≤i<j≤n 且 s[j]−s[i]=k
  3. 写成s[i]=s[j]-k,,这就是两数之和,利用哈希表"枚举右,维护左"
    • 枚举当前的前缀和 s[j],看看曾经有多少个前缀和等于 s[j]−k(配对)
    • 比如 s[j]=2,那么 s[i]=s[j]−k=2−1=1,我们要找的是 j左边有多少个 s[i]=1即可。这个操作可以使用哈希表
写法1:两次遍历
java 复制代码
class Solution {
  public int subarraySum(int[] nums, int k) {
    //统计前缀和
    int n = nums.length;
    int[] s = new int[n + 1];
    for (int i = 0; i < n; i++) {
      s[i + 1] = s[i] + nums[i];
    }

    //维护左,枚举右
    Map<Integer,Integer> cnt = new HashMap<>();
    int ans = 0;
    for(int x : s){
      ans += cnt.getOrDefault(x - k,0);
      cnt.merge(x,1,Integer::sum);
    }
    return ans;
  }
}
写法二:一次遍历
  • 我们可以一边计算前缀和,一边遍历前缀和。

  • 在遍历 nums 之前,我们需要先记录 s0=0,即空前缀的元素和等于 0。往 cnt 中添加 cnt0=1。

java 复制代码
class Solution {
  public int subarraySum(int[] nums, int k) {
    Map<Integer, Integer> cnt = new HashMap<>(nums.length + 1, 1); // 预分配空间
    cnt.put(0, 1); // s[0]=0 单独统计
    int s = 0;
    int ans = 0;
    for (int x : nums) {
      s += x;
      ans += cnt.getOrDefault(s - k, 0);
      cnt.merge(s, 1, Integer::sum); // cnt[s]++
    }
    return ans;
  }
}
写法三:一次遍历
  • 在同一轮循环中,先把 s *i* −1 加入哈希表,再根据 s *i* 更新答案。
  • 这样写无需初始化 cnt0=1。
java 复制代码
class Solution {
  public int subarraySum(int[] nums, int k) {
    Map<Integer, Integer> cnt = new HashMap<>(nums.length, 1); // 预分配空间
    int s = 0;
    int ans = 0;
    for (int x : nums) {
      cnt.merge(s, 1, Integer::sum); // cnt[s]++
      s += x;
      ans += cnt.getOrDefault(s - k, 0);
    }
    return ans;
  }
}

如果这篇文章对你有帮助,欢迎点赞、评论、关注、收藏。你们的支持是我前进的动力!

相关推荐
To_OC3 小时前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安8 小时前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者10 小时前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent
北域码匠14 小时前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
kisshyshy17 小时前
从递归到迭代,一文吃透二叉树的核心知识与 JavaScript 实现
javascript·算法·代码规范
To_OC1 天前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
用户938515635071 天前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
To_OC1 天前
手写快排次次翻车?别死背快排模板了,这才是面试官想听的底层逻辑
javascript·算法·排序算法