题目链接:https://leetcode.com/problems/subarrays-with-k-different-integers/
题意:已知一个全为正数的数组A,1<=A.length<=20000,1<=A[i]<=A.length,1<=K<=A.length,问A中恰好有K个不同的元素的子数组个数有多少个
思路:对于考虑到数据范围较大,暴力显然不可取。对于每个位置i,如果用min_k[i] 表示起始位置为i的子数组存在k个元素的最小结束位置,用min_k_1[i] 表示起始位置为i的子数组存在k+1个不同的元素的最小结束位置,则对于起始位置为i、存在k个不同元素的的子数组的个数为min_k_1[i]-min_k[i]。
至于如何求min_k[i]呢(和求min_k_1的方法一样)呢,可以维护一个滑动窗口,左边界为i,当滑动窗口不同元素个数小于k时,向右移动右边界,当等于k时,则左边界变为i+1,刚好用来求min_k[i+1],min_k[i+1]>=min_k[i],因此可在线性的时间内求出min_k和min_k_1,滑动窗口的不同元素个数可以用map求(无序的话用inordered_map更好)。
class Solution { public: int subarraysWithKDistinct(vector<int>& A, int K) { if (K > A.size()||K==0) return 0;int *min_k = new int[A.size()], *min_k_1 = new int[A.size()]; cal_min(A,min_k,A.size(),K); //计算min_k cal_min(A, min_k_1, A.size(), K + 1); //计算min_k_1 int ans = 0; for (int i = 0;i < A.size();i++) { if (min_k_1[i] + min_k[i] == -2) //如果都未更新,则i起始的子数组不同元素个数一定小于K continue; else if(min_k_1[i]!=-1) //min_k_1更新 ans += min_k_1[i] - min_k[i]; else ans += A.size() - min_k[i]; //min_k_1未更新,min_k更新,最后一个元素依然长度为K } return ans; } void cal_min(vector<int>& A,int *Min,int len,int K) { map<int, int> mp; memset(Min, -1, 4 * len); for (int i = 0;i < K;i++) mp[A[i]]++; int l = 0, r = K - 1; while (r < A.size()) { while (mp.size() == K) { Min[l] = r; mp[A[l]]--; if (mp[A[l]] == 0) mp.erase(A[l]); l++; } r++; if (r<A.size()) mp[A[r]]++; } } };