华为od真题-2022.11.5-k优雅阈值 - 中等

k优雅阈值

Problem Description

如果一个数组中出现次数最多的元素出现大于等于k 次, 被称为k−优雅数组 ,k也可以被称为优雅阈值。例如,数组[1,2,3,1,2,3,1],它是个3−优雅数组 ,因为元素 1 出现次数大于等于 3 次,数组[1,2,3,1,2] 就不是一个3−优雅数组, 因为其中出现次数最多的元素是 1 和 2,只出现了 2 次。 给定一个数组 A 和 k ,请求出 A 有多少子数组是k−优雅子数组 。
子数组是数组中一个或多个连续元素组成的数组。例如,数组 [1,2,3,4] 包含 10个子数组,分别是:[1] , [1,2] , [1,2,3] , [1,2,3,4] , [2,3] , [2,3,4] , [3,4] ,[4]

input

第一行输入两个整数 n≤10000 和 k≤n 。1≤ai≤n
第二行输入n 个整数。

ouput

输出给定的数组中有多少子数组是 k−优雅子数组

Sample Input

7 3
1 2 3 1 2 3 1

Sample Output

1

题目类型、难度、来源

总体思路:

  • 因为要求k-优雅数组的数量,用暴力法的话,时间复杂度能达到O(2n),不可行。可以考虑使用滑动窗口。使用滑动窗口主要考虑几个问题:窗口何时扩张、何时收缩、何时移动。
    • 在窗口向右移动的情况下,收缩是指left指针右移,即向右收缩。扩张是指right指针右移,即向右扩张。移动是指left指针和right指针同时右移
  • 初始时,窗口大小为1,left指针和right指针都指向0。此时窗口只有一个元素,并将这个元素出现的次数使用一个cnt数组记录下来。记录此时出现最多的元素为max_num,出现最多的元素的次数为max_cnt;
  • 操作前,先检查max_cnt是否达到了k,如果没达到,说明当前窗口不满足k-优雅数组。此时就不能收缩(因为收缩后)数组变小了,是原来的窗口数组的子集,原来都没达到条件,收缩后就更不可能了。此时也不能将窗口向右移动,因为这可能会错过一些可能的情况。因此只能向右扩张
  • 如果max_cnt达到了k。那么说明当前窗口满足k-优雅数组。因为窗口每次向右扩张都检查一次max_cnt,所以max_cnt只能等于k,也就是只能算刚刚满足k-优雅数组的条件,也就是新加入的right所指的元素令当前窗口数组满足了k-优雅数组。因此这时就不能马上向右扩张或向右移动,这会导致遗漏。应该考虑向右收缩,因为最左边的元素可能去掉,也同样满足k-优雅数组,我们要找到最小的满足k-优雅阈值的窗口。但是在向右收缩时,要记录收缩的步数c(用于计算包含该最小k-优雅数组的数组个数,因为最小k-优雅数组都满足条件,那么包含该数组的数组肯定也满足条件)。收缩到最小后,可以计算包含right指针所指元素在内,右边一共有n-right个元素;而左边包含left指针所指元素在内,一共有c+1个元素(从收缩前开始算)。根据乘法原理包含目前最小k-优雅数组的数组总数(这不是所有,但为了避免重复,凡是left遍历过的元素,都不可能再次访问,我们可以将其丢弃,因此只用收缩前的数组计算数组总数)为(c+1)*(n-right)。计算完后,将整个窗口向右移动。

例子

  • 有大小为8的数组[1,2,3,1,1,2,3,1],要找3-优雅数组。初始时left和right都指向第0个元素
    在这里插入图片描述
  • 按照我们的流程,此时应该向右扩张,直到满足最小k优雅数组。向右扩张一位,发现并不能满足要求,因为出现最多的元素1和2只出现了一次。
    在这里插入图片描述
  • 继续向右扩张,直到right=4。此时发现出现最多的元素1出现了3次,因此当前数组满足3-优雅数组。
    在这里插入图片描述
  • 此时还不能直接计算数组数,应该向右扩张,但发现向右扩张后就不能满足k优雅数组,因此现在的窗口就是最小的k-优雅数组。收缩了0步,因此c=0。因为right向右扩张得到的数组包含当前数组,因此这些数组也是满足k-优雅数组的,所以这些数组也不能漏掉,包含right在内的右边的元素一共有n-right=4个。所以result要加上(n-right)*(c+1)=4。
  • 窗口向右移动,发现当前窗口不满足k-优雅数组。因此要向右扩张,直到满足条件。
    在这里插入图片描述
  • 当right移动到7的时候,发现满足条件。
    在这里插入图片描述
  • 此时因为导致窗口满足k-优雅数组的元素是1,而left所指的元素是2,因此当前窗口并不是最小k-优雅数组,应该向右收缩,并记录收缩的步数c。
    在这里插入图片描述
  • 当left向右收缩到3时,发现当前数组就是最小k-优雅数组。(left等于1和2时,因为包含当前窗口,因此也属于k-优雅数组)于是可以计算(n-right)*(c+1)=3。
  • 可以得出最后k-优雅数组为3+4=7个。分别是[1,2,3,1,1], [1,2,3,1,1,2], [1,2,3,1,1,2,3], [1,2,3,1,1,2,3,1]和[2,3,1,1,2,3,1], [3,1,1,2,3,1], [1,1,2,3,1]

AC代码

#include <iostream>
#include <algorithm>
using namespace std;
int main(){
    
    
	int n, k;
	cin >> n >> k;
	int *a = new int[n+100];
	for (int i = 0; i < n; i++){
    
    
		cin >> a[i];
	}
	long long result = 0;
	int left = 0, right = 0;
	int cnt[n+100] = {
    
    0}, max_num, max_cnt = 0;
	cnt[a[0]]++;
	max_num = a[0];
	max_cnt = cnt[a[0]];
	while (right != n){
    
    
		if (max_cnt >= k){
    
    
			int c = 0;
			while (left <=right){
    
    
				if (a[left] != max_num){
    
    
					left++;
					cnt[a[left-1]]--;
					c++;
				}else{
    
    
					break;
				}
			}
			result += (n-right)*(c+1);
			// move right
			left++;
			right++;
			cnt[a[left-1]]--;
			cnt[a[right]]++;
			if (a[left-1] == max_num){
    
    
				max_cnt--;
			}
		}else{
    
    
			// expend
			right++;
			cnt[a[right]]++;
		}
		if (cnt[a[right]] > max_cnt){
    
    
			max_cnt = cnt[a[right]];
			max_num = a[right];
		}
	}
	cout << result;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40735291/article/details/129646055