先给题
给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组。
(例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。)
返回 A 中好子数组的数目。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subarrays-with-k-different-integers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个也就不详谈了,一个一个去比较,结果肯定超时。
当我看到子数组时,我想到了滑动窗口的解法,之前我也做过这样的题了,在https://www.cnblogs.com/lisuhang/p/14386502.html,没看的可以看一下。
我个人的解法其实是对暴力的一种优化,(做的题太少,没有想到关键点)
假设此时我们的子数组是[i,j],那么下一个j+1,如何判断[i,j + 1]符不符合题意呢?让第j+1个元素与前面出现过的元素比较,如果没出现过,那么sum++,出现过则sum的值不变。
接下来只要判断sum的值符不符合K了,如果符合,那么我们可以继续向右边滑动,如果不符合,那么我们就要左边滑动缩小范围。
原理上是这样的,但当我们左指针滑动时,去除的那个数对sum的值有没有影响呢?我们还需要再判断。如果依次比较,太耗时间了。我在这里想了一个影响值的设定。
影响值是对sum的影响值。打个比方,一开始有一个1,那么这个1 对sum的影响值 就是1,它的加入上sum++了。接下来变成1 2,2与1不同,所以2的影响值也是1,1的影响值不变,接下来变成 1 2 1,那么此时第一个1的影响值就变为0,因为它的加入是不影响sum的值的。
然后我们在保证每时每刻子数组第一位数的影响值始终是1,那么只要做窗口移动,sum的值一定会受到影响。
如果仅这样做,我们会丢掉一些符合条件的子数组,就拿上面的例子 1 2 1 如果K=2,那么符合的子数组有三个 1 2,2 1,1 2 1.而按照我的影响值,结果就成了1 2和2 1,忽略掉了 1 2 1。那么我们怎么来解决这个问题呢?
我加了一个zero的值来记录我们到底忽略了前面影响值为0的元素数目。只要是因为首元素影响值为0而左窗口移动的,我们就让zero++(影响值为0,加不加都不会影响sum值,但加上它也是一个符合的子数组),只有因为sum>k 而左窗口移动时,zero重置为0(因为此时我们跳过了一个影响值为1的元素,此时是超过K了,所以前面的zero所记录的值就没有用了)。
接下来就是影响值如何设置了,我们需要右窗口指针指向的元素数值从j-1开始到i依次比较,只要遇到相同的那么就把匹配到的这个元素的影响值设为0,停止循环,并给右端元素的影响值赋值为1,此时sum值不变;如果没遇到,那么sum++。
我们知道,当左窗口移动而右窗口没有移动的时候,此时我们无需再判断影响值。那么只要是右窗口移动,而且j的值在规定范围中,我们便让它去判断影响值,否则不去判断。
int subarraysWithKDistinct(vector<int>& A, int K) {
if (K == 0) return 0;
int n = 0;//记录好子数组的数量
int sum = 0;//记录当前子数组的不同元素个数之和
int zero = 0;
int* sum_add = new int[A.size()];
int i = 0, j = 0;
int flag = 1;
sum_add[i] = 1;
while (i <= j) {
if (flag) {
for (int s = j - 1; s >= i; s--) {
if (A[j] == A[s]) {
sum_add[s] = 0;
sum--;//后面sum++,因为有相同的,sum就不再改变,所以这里让它--;
break;
}
}
sum++;
sum_add[j] = 1;
}
while (sum_add[i] == 0) {
i++;
zero++;
}
if (sum == K)
n += zero + 1;
if (sum <= K && j < A.size() - 1) {
j++;
flag = 1;
}
else {
zero = 0;
sum--;
i++;
flag = 0;
}
}
return n;
}
这是我写的代码,因为越界问题,做了很久。(晚上静了静才终于搞定了)。
通过是通过了,而且内存方面也没太大问题,但是速度方面却只是勉强通过。
原文:https://www.cnblogs.com/lisuhang/p/14394680.html