1.6927 minimum index of a validsplit
353周leetcode周赛题、中等
题目:
如果元素 x 在长度为 m 的整数数组 arr 中满足 freq(x) * 2 > m ,那么我们称 x 是 支配元素 。其中 freq(x) 是 x 在数组 arr 中出现的次数。注意,根据这个定义,数组 arr 最多 只会有 一个 支配元素。
给你一个下标从 0 开始长度为 n 的整数数组 nums ,数据保证它含有一个支配元素。
你需要在下标 i 处将 nums 分割成两个数组 nums[0, …, i] 和 nums[i + 1, …, n - 1] ,如果一个分割满足以下条件,我们称它是 合法 的:
0 <= i < n - 1
nums[0, …, i] 和 nums[i + 1, …, n - 1] 的支配元素相同。
这里, nums[i, …, j] 表示 nums 的一个子数组,它开始于下标 i ,结束于下标 j ,两个端点都包含在子数组内。特别地,如果 j < i ,那么 nums[i, …, j] 表示一个空数组。
请你返回一个 合法分割 的 最小 下标。如果合法分割不存在,返回 -1 。
示例 1:
输入:nums = [1,2,2,2]
输出:2
解释:我们将数组在下标 2 处分割,得到 [1,2,2] 和 [2] 。
数组 [1,2,2] 中,元素 2 是支配元素,因为它在数组中出现了 2 次,且 2 * 2 > 3 。
数组 [2] 中,元素 2 是支配元素,因为它在数组中出现了 1 次,且 1 * 2 > 1 。
两个数组 [1,2,2] 和 [2] 都有与 nums 一样的支配元素,所以这是一个合法分割。
下标 2 是合法分割中的最小下标。
示例 2:
输入:nums = [2,1,3,1,1,1,7,1,2,1]
输出:4
解释:我们将数组在下标 4 处分割,得到 [2,1,3,1,1] 和 [1,7,1,2,1] 。
数组 [2,1,3,1,1] 中,元素 1 是支配元素,因为它在数组中出现了 3 次,且 3 * 2 > 5 。
数组 [1,7,1,2,1] 中,元素 1 是支配元素,因为它在数组中出现了 3 次,且 3 * 2 > 5 。
两个数组 [2,1,3,1,1] 和 [1,7,1,2,1] 都有与 nums 一样的支配元素,所以这是一个合法分割。
下标 4 是所有合法分割中的最小下标。
示例 3:
输入:nums = [3,3,3,3,7,2,2]
输出:-1
解释:没有合法分割。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
nums 有且只有一个支配元素。
题解:
我们需要遍历切割点,将数组分为左右,并且记录左右两边的支配元素(以及有无)。
每次切割都去计算一次两边的支配元素?
后发现每次迭代后在上一次迭代的基础作出相应调整即可:
- 用两个HashMap记录左右两边的数字和出现次数
维护:-切割点往右移一位的同时,- 在左边hashMap中加入这个数字:hashMap中已有,则value+1,若暂无,则put,value为1;
- 在右边hashMap中将对应的value-1。
- 记录两边最多的数字max_l,max_r
- 原因:找左右两边List的支配元素时,若存在,则必为出现最多的元素;那么判断max_l/max_r是否满足支配元素的条件就可知两边是否有支配元素
- 维护:切割点往右移一位的同时,
- 如果变动的数字与左边max_l相同,则max_l不变,否则可能改变,重新选出max_l
- 如果变动的数字与右边max_r不同,则max_r不变,否则可能改变,重新选出max_r
最后判断即可:
2*left_map.get(max_l)>i+1&&2*right_map.get(max_r)>nums.size()-i-1&&max_l==max_r
代码
public static int minimumIndex(List<Integer> nums) {
HashMap<Integer,Integer> left_map = new HashMap<>();
HashMap<Integer,Integer> right_map = new HashMap<>();
//初始化right_map,以及找出max_r,初始状态右边List即使整个List
for(int i=0;i<nums.size();++i){
right_map.put(nums.get(i),right_map.get(nums.get(i))!=null? right_map.get(nums.get(i))+1:1);
}
int max_r=nums.get(0);
for(int i:right_map.keySet()){
if(right_map.get(i)>right_map.get(max_r)){
max_r=i;
}
}
int max_l=nums.get(0);
for(int i=0;i<nums.size()-1;++i){
//调整左右两边的hashMap
left_map.put(nums.get(i),left_map.get(nums.get(i))!=null?left_map.get(nums.get(i))+1:1);
right_map.put(nums.get(i),right_map.get(nums.get(i))-1);
//若变动的元素不是左边最大值,则max_l可能变化,重新计算
if(nums.get(i)!=max_l){
if(left_map.get(nums.get(i))>left_map.get(max_l)){
max_l = nums.get(i);
}
}
//若变动的元素是右边最大值,移向左边,则max_r可能变化,重新计算
if(nums.get(i)==max_r){
for(int m:right_map.keySet()){
if(right_map.get(m)>right_map.get(max_r)){
max_r = m;
}
}
}
//判断两边是否有支配元素且相同
if(2*left_map.get(max_l)>i+1&&2*right_map.get(max_r)>nums.size()-i-1&&max_l==max_r){
return i;
}
}
return -1;
}
提交结果:
912/915,超时!!算法依然性能不够
后发现没有对题目所给条件进行优化
正确版本
因为要求两个支配元素相同,
所以只需先找到出现最多的元素以及出现次数(利用hashMap); 接着在遍历过程中
- 记录该元素在左边出现次数(也得到了在右边的出现次数count-count_l)
- 判断是否同时是两边的支配元素
修订代码
public static int minimumIndex(List<Integer> nums) {
HashMap<Integer,Integer> map = new HashMap<>();
int max=nums.get(0);
int count = 1;
//初始化map,以及找出出现最多的元素max,以及它出现的次数count
for(int i=0;i<nums.size();++i){
map.put(nums.get(i),map.get(nums.get(i))!=null? map.get(nums.get(i))+1:1);
}
for(int i:map.keySet()){
if(map.get(i)>count){
max=i;
count=map.get(i);
}
}
//不在需要记录右边的,因为count_r=count-count_l
int count_l=0;
for(int i=0;i<nums.size()-1;++i){
//如果变动了max
if(nums.get(i)==max){
count_l++;
}
//判断两边是否有支配元素且相同
if(2*count_l>i+1&&2*(count-count_l)>nums.size()-i-1){
return i;
}
}
return -1;
}
2. 6929 Maximum Beauty of an Array After Applying Operation
353周leetcode周赛题、中等
题目:
给你一个下标从 0 开始的整数数组 nums 和一个 非负 整数 k 。
在一步操作中,你可以执行下述指令:
在范围 [0, nums.length - 1] 中选择一个 此前没有选过 的下标 i 。
将 nums[i] 替换为范围 [nums[i] - k, nums[i] + k] 内的任一整数。
数组的 美丽值 定义为数组中由相等元素组成的最长子序列的长度。
对数组 nums 执行上述操作任意次后,返回数组可能取得的 最大 美丽值。
注意:你 只 能对每个下标执行 一次 此操作。
数组的 子序列 定义是:经由原数组删除一些元素(也可能不删除)得到的一个新数组,且在此过程中剩余元素的顺序不发生改变。
题解
由子序列的定义分析可知,因为可以删除一些元素,所以可以理解为我们可以从中“自由的挑选”
原数组的最大美丽值和排序后的最大美丽值相等,分析如下:
而对于排序后数组的最大美丽值,就很好找了。只要找到最大的 右边界-左边界>2k的 区间即可,
代码:
//实现的并不太好,看个乐子就行
public int maximumBeauty(int[] nums, int k) {
Arrays.sort(nums);
int len=1;
for(int i=0;i<nums.length;++i){
int j=i+1;
while(j<nums.length&&nums[j]-nums[i]<=2*k){
++j;
}
len = Math.max(j - i, len);
if(j>=nums.length){
return len;
}
while(i<nums.length&&nums[j]-nums[i]>2*k){
++i;
}
i--;
}
return len;
}
可以采用滑动窗口的方式解决