leetcode
leetcode 451 ~ 500
1 比 0 多的子数组个数

1 比 0 多的子数组个数

难度:

标签:

题目描述

代码结果

运行时间: 107 ms, 内存: 21.3 MB


/*
题目思路:
1. 使用流处理数组。
2. 将数组转换为1和-1(0变为-1)。
3. 使用前缀和记录当前的1和-1数量差。
4. 使用一个哈希表来记录当前差值出现的次数。
5. 最后累加满足条件的子数组个数。
*/
import java.util.HashMap;
import java.util.stream.IntStream;
 
public class SubarrayCountStream {
    public int subarraysWithMoreOnesThanZeros(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        int[] diff = {0};
        return IntStream.of(nums).map(num -> num == 1 ? 1 : -1).reduce(0, (count, num) -> {
            diff[0] += num;
            count += map.getOrDefault(diff[0], 0);
            map.put(diff[0], map.getOrDefault(diff[0], 0) + 1);
            return count;
        });
    }
 
    public static void main(String[] args) {
        SubarrayCountStream scs = new SubarrayCountStream();
        int[] nums = {1, 0, 1, 0, 1};
        System.out.println(scs.subarraysWithMoreOnesThanZeros(nums)); // Output: 9
    }
}
 

解释

方法:

该题解使用前缀和的思路来解决问题。通过维护一个前缀和数组 cnts,其中 cnts[i] 表示前缀和为 i 的子数组个数。遍历数组 nums,对于每个元素,如果是 1,则将前缀和加 1;如果是 0,则将前缀和减 1。对于当前前缀和 s,cnt 变量累加 cnts[s],表示以当前元素结尾、满足条件的子数组个数。最后将 cnt 累加到答案 ans 中,并对 10^9 + 7 取模。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
题解中提到的前缀和数组cnts用来记录前缀和的子数组个数,但如何处理可能出现的负数前缀和?
在题解中,前缀和数组 cnts 的大小被设置为 2 * len(nums) + 1,这是为了确保即使前缀和为负数,也能在数组中正确索引。前缀和数组的中间索引(len(nums))代表前缀和为0的位置。当前缀和为正时,索引为 len(nums) + s;当前缀和为负时,索引为 len(nums) - |s|。这种映射方法允许我们在同一数组中处理正数和负数前缀和,避免了使用哈希表等其他更复杂的数据结构。
🦆
在题解逻辑中,当遍历到数字0时,为什么需要从cnt变量中减去cnts[s]?这样的操作是基于什么考虑?
在题解的逻辑中,当元素为0时,前缀和 s 减 1,这意味着我们正在探索尾部含有更多0的子数组。在这种情况下,我们需要从 cnt 中减去 cnts[s],因为 cnts[s] 记录的是以当前 s 为前缀和的子数组个数,这个操作是为了剔除那些不满足“1比0多”的子数组计数。简而言之,这是为了确保 cnt 变量仅包括满足题目要求的子数组数,从而准确地统计符合条件的子数组。
🦆
题解中使用了取模操作(10^9 + 7),这种操作在算法中通常有什么作用?为什么需要在这个问题中使用它?
取模操作通常用于处理大数运算,以防止整数溢出并减小处理数的规模。在许多编程比赛和实际应用中,答案很大时常常要求对一个大的质数(如 10^9 + 7)取模,以保持结果的可管理性并避免数据类型的限制。在本题中,使用取模操作是为了确保计算过程中和结果的数值不会超出编程语言处理整数的范围,同时满足可能的输出要求。
🦆
题解中的前缀和s是如何在遍历过程中更新的,这种更新方式与子数组的计数有什么直接关系?
在题解中,前缀和 s 根据当前遍历的元素更新:如果是1,则 s 增加1;如果是0,则 s 减少1。这种更新方式直接关联到子数组的计数,因为它反映了从数组开始到当前元素的1和0的净差值。前缀和的变化用于识别数组中1比0多的部分,通过前缀和数组 cnts 记录和追踪特定前缀和值出现的次数,从而计算满足条件的子数组个数。每次 s 更新后,通过 cnts[s] 的值可以直接得知当前前缀和值对应的子数组数量,这是计算满足条件子数组的核心逻辑。

相关问题