2155. 分组得分最高的所有下标
给你一个下标从 0 开始的二进制数组 nums ,数组长度为 n 。nums 可以按下标 i( 0 <= i <= n )拆分成两个数组(可能为空):numsleft 和 numsright 。
numsleft 包含 nums 中从下标 0 到 i - 1 的所有元素(包括 0 和 i - 1 ),而 numsright 包含 nums 中从下标 i 到 n - 1 的所有元素(包括 i 和 n - 1 )。
如果 i == 0 ,numsleft 为 空 ,而 numsright 将包含 nums 中的所有元素。
如果 i == n ,numsleft 将包含 nums 中的所有元素,而 numsright 为 空 。
下标 i 的 分组得分 为 numsleft 中 0 的个数和 numsright 中 1 的个数之 和 。
返回 分组得分 最高 的 所有不同下标 。你可以按 任意顺序 返回答案。
示例 1:
输入:nums = [0,0,1,0]
输出:[2,4]
解释:按下标分组
- 0 :numsleft 为 [] 。numsright 为 [0,0,1,0] 。得分为 0 + 1 = 1 。
- 1 :numsleft 为 [0] 。numsright 为 [0,1,0] 。得分为 1 + 1 = 2 。
- 2 :numsleft 为 [0,0] 。numsright 为 [1,0] 。得分为 2 + 1 = 3 。
- 3 :numsleft 为 [0,0,1] 。numsright 为 [0] 。得分为 2 + 0 = 2 。
- 4 :numsleft 为 [0,0,1,0] 。numsright 为 [] 。得分为 3 + 0 = 3 。
下标 2 和 4 都可以得到最高的分组得分 3 。
注意,答案 [4,2] 也被视为正确答案。
示例 2:
输入:nums = [0,0,0]
输出:[3]
解释:按下标分组
- 0 :numsleft 为 [] 。numsright 为 [0,0,0] 。得分为 0 + 0 = 0 。
- 1 :numsleft 为 [0] 。numsright 为 [0,0] 。得分为 1 + 0 = 1 。
- 2 :numsleft 为 [0,0] 。numsright 为 [0] 。得分为 2 + 0 = 2 。
- 3 :numsleft 为 [0,0,0] 。numsright 为 [] 。得分为 3 + 0 = 3 。
只有下标 3 可以得到最高的分组得分 3 。
示例 3:
输入:nums = [1,1]
输出:[0]
解释:按下标分组
- 0 :numsleft 为 [] 。numsright 为 [1,1] 。得分为 0 + 2 = 2 。
- 1 :numsleft 为 [1] 。numsright 为 [1] 。得分为 0 + 1 = 1 。
- 2 :numsleft 为 [1,1] 。numsright 为 [] 。得分为 0 + 0 = 0 。
只有下标 0 可以得到最高的分组得分 2 。
提示:
n == nums.length
1 <= n <= 10^5
nums[i] 为 0 或 1
Solution1:
此类题目由数据范围可知,暴力的做法会超时,因此我们需要摸索一下规律;
- 由于这题是按照区间以及0,1的个数来计算的分数,因此我们可以想到利用前缀和思想,对每个区间的0,1的个数进行保存,有了这样一个前缀和数组后,我们即可很容易的计算得出以每一个下标作为起点进行尝试所得到的分数,接着对这些分数找到最大值,再找到最大值对应的几个起点下标即可.
- 具体过程为造出前缀和数组并赋值,计算以每个下标作为起点的分数,寻找最大分数此时的起点下标;
Code(双前缀和数组形式保存):
class Solution {
public List<Integer> maxScoreIndices(int[] nums) {
int[][] prefixCount = new int[nums.length][2];
if(nums[0] == 0){
prefixCount[0][0] = 1;
}
else{
prefixCount[0][1] = 1;
}
for(int i=1;i<nums.length;i++){
if(nums[i] == 1){
prefixCount[i][1] = prefixCount[i-1][1] + 1;
prefixCount[i][0] = prefixCount[i-1][0];
}
if(nums[i] == 0){
prefixCount[i][0] = prefixCount[i-1][0] + 1;
prefixCount[i][1] = prefixCount[i-1][1];
}
}
List<Integer> list = new ArrayList<>();
int temp = -1;
for(int i=0;i<= nums.length;i++){
if(i == 0){
nums[i] = prefixCount[nums.length-1][1];
}
else if(i == nums.length){
temp = prefixCount[nums.length-1][0];
}
else{
nums[i] = prefixCount[i-1][0] + prefixCount[nums.length-1][1] - prefixCount[i-1][1];
}
}
int max = nums[0];
for(int i=1;i<=nums.length;i++){
if(i == nums.length){
if(temp > max){
max = temp;
}
continue;
}
if(nums[i] > max){
max = nums[i];
}
}
for(int i=0;i<=nums.length;i++){
if(i == nums.length){
if(max == temp){
list.add(i);
}
continue;
}
if(max == nums[i]){
list.add(i);
}
}
return list;
}
}
Solution2:
- 但是这题的话,我们没必要0和1的前缀和均进行保存,我们是从最左边作为起点来遍历的,那么我们只需要保存数组中总共含有的1的个数,然后随着起点的不断右移,根据右移时得到1还是放弃1,得到0还是放弃0,即可将0和1的个数同样的进行维护,而在维护后可以直接算出此时的总分,直接与假设的maxScore进行比较,从而少了很多过程;
Code(利用滚动数组思想->单前缀和变量形式保存):
class Solution {
public List<Integer> maxScoreIndices(int[] nums) {
int l = 0; //存0
int r = 0; //存1
for(int i=0;i<nums.length;i++){
if(nums[i] == 1){
r++;
}
}
int maxScore = r;
List<Integer> list = new ArrayList<>();
list.add(0); //此时拿的maxScore是全部的1的个数,按照题意即此时的起点为0
for(int i=0;i<nums.length;i++){
//i从0到nums.length-1进行枚举,但是实际枚举的起点是i+1.(由题目的分割原理可知)
if(nums[i] == 0){
l++;
}
else if(nums[i] == 1){
r--;
}
if(l+r > maxScore){
list.clear();
list.add(i+1);
maxScore = l+r;
}
else if(l+r == maxScore){
list.add(i+1);
}
}
return list;
}
}