统计完全子数组的数目
给你一个由 正 整数组成的数组 nums 。
如果数组中的某个子数组满足下述条件,则称之为 完全子数组 :
子数组中 不同 元素的数目等于整个数组不同元素的数目。
返回数组中 完全子数组 的数目。
子数组 是数组中的一个连续非空序列。
示例 1:
输入:nums = [1,3,1,2,2]
输出:4
解释:完全子数组有:[1,3,1,2]、[1,3,1,2,2]、[3,1,2] 和 [3,1,2,2] 。
示例 2:
输入:nums = [5,5,5,5]
输出:10
解释:数组仅由整数 5 组成,所以任意子数组都满足完全子数组的条件。子数组的总数为 10 。
提示:
\(1 <= nums.length <= 1000\)
\(1 <= nums[i] <= 2000\)
解题思路1:暴力解
本来没想使用暴力解的,看着这个题目就像是双指针的问题。但是看着这个数据量,算了一下时间复杂度以及周赛的排名,还是默默写下了这个违背初心的解法。。。。关键:统计不同元素的数目时不要使用set,因为set的插入是O(log(n))的,需要使用map,插入的时间复杂度是O(1)的。
code1
class Solution {
public:
//子数组的不同元素的数目和整个数组的不同元素数目相同
//双重遍历判断是否符合条件
int countCompleteSubarrays(vector<int>& nums) {
int ans = 0;
unordered_map<int,int> cnt;
for(auto item : nums) cnt[item] ++;
for(int i = 0;i < nums.size();i ++)
{
unordered_map<int,int> has;
for(int j = i;j < nums.size();j ++)
{
has[nums[j]] ++;
int len1 = cnt.size();
int len2 = has.size();
if(len1 == len2)
{
ans += nums.size() - j;
break;
}
}
}
return ans;
}
};
解题思路2:双指针
双指针的解法还是从朴素的写法出发,朴素解时间性能瓶颈主要是在右指针需要不断回溯,只要右指针不需要回溯即可。
- 枚举左指针为起点的完全子数组的数目
- 在枚举过程中右指针不回溯
code2
class Solution {
public:
//子数组中不同元素的数目和原数组相同
//双指针
//如何思考呢?
//朴素的解法:遍历每一个子数组判断子数组中是否符合条件
//性能不高的原因在于右指针需要回溯
//是否可以让右指针不需要回溯呢
//是有方法可以使得右指针不需要回溯
//比如找到一个符合条件的子数组时,就可以直接计算当前左指针为起点的完全子数组的数目
//右移左指针,查找下一个一左指针为起点的子数组的数目
//这个时候是不需要回溯的,只需要判断是不是缺少了不同的元素即可
//写的过程还是不够丝滑很多细节都没能把握好
//还是需要理清思路再下手写
int countCompleteSubarrays(vector<int>& nums) {
int ans = 0;
int len = nums.size();
unordered_map<int,int> cnt;
for(auto item : nums) cnt[item] ++;
int l = 0,r = 0;
unordered_map<int,int> has;
has[nums[r]] ++;
while(l < nums.size() && r < nums.size())
{
while(has.size() < cnt.size())
{
r++;
if(r < nums.size())
has[nums[r]] ++;
else break;
}
if(has.size() == cnt.size()) ans += len - r;
has[nums[l]] --;
if(has[nums[l]] == 0) has.erase(nums[l]);
l ++;
}
return ans;
}
};
浙公网安备 33010602011771号