算法(JS):统计完全子数组的数目

49 阅读2分钟

题目链接

2799. 统计完全子数组的数目

题目描述

给你一个由  整数组成的数组 nums 。

如果数组中的某个子数组满足下述条件,则称之为 完全子数组 :

  • 子数组中 不同 元素的数目等于整个数组不同元素的数目。

返回数组中 完全子数组 的数目。

子数组 是数组中的一个连续非空序列。

分析

不同元素的数目元素的种类数量。因此, 完全子数组 其实就是包含 nums 中各不同数字 至少一个 的子数组。

显然,若子数组 [left, right]完全子数组 ,那么子数组 [left, r], (right < r < nums.length) 也是 完全子数组 。若子数组 [left, right] 不是 完全子数组,那么它的所有 子数组 全都不是 完全子数组。即 子数组长度满足题目条件 之间存在 单调性

解法

先遍历一遍 nums,统计总共有多少种数字。再将所有子数组分类为 以下标0开头的以下标1开头的 ...,即采用 滑动窗口 ,逐步右移子数组的 左边界,分别统计左边界确定的条件下, 完全子数组 的数目,最后累加即可。

复杂度

  • 时间复杂度: O(n)O(n)
  • 空间复杂度: O(n)O(n)

代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var countCompleteSubarrays = function(nums) {
    // 统计总共的种数
    const typeCount = (new Set(nums)).size;

    let res = 0;
    const numberRecord = new Map(); 
    // 记录窗口的当前状态, key为数字,value为该数字在窗口中出现的次数。显然,map.size为数字的种类
    for(let left = 0, right = 0; left < nums.length; left++) {
        while(numberRecord.size < typeCount && right < nums.length) {
            // right入窗口
            numberRecord.set(nums[right], (numberRecord.get(nums[right]) ?? 0) + 1);
            right++;
        }
        if(numberRecord.size < typeCount) {
            // 后续已无满足要求的子数组
            break;
        }
        res += nums.length - right + 1;
        // left出窗口
        const leftNumberCount = numberRecord.get(nums[left]);
        if(leftNumberCount === 1) {
            numberRecord.delete(nums[left]);
        } else {
            numberRecord.set(nums[left], leftNumberCount - 1);
        }
    }
    return res;
};