2026-04-29:二进制交换后的最大分数。用go语言,给定一个长度为 n 的整数数组 nums 和一个长度相同的二进制字符串 s。
初始得分为 0。对于字符串中每个位置上字符为 '1' 的下标 i,分数都会加上 nums[i]。
你可以进行任意次操作,也可以一次都不做。每次操作时,可以选择一个位置 i(0 <= i < n - 1),要求 s[i] = '0' 且 s[i + 1] = '1',然后把这两个字符交换。
请计算并返回经过这些操作后,能够得到的最高分数。
n == nums.length == s.length。
1 <= n <= 100000。
1 <= nums[i] <= 1000000000。
s[i] 是 '0' 或 '1'。
输入: nums = [2,1,5,2,3], s = "01010"。
输出: 7。
解释:
我们可以执行以下交换操作:
在下标 i = 0 处交换:"01010" 变为 "10010"
在下标 i = 2 处交换:"10010" 变为 "10100"
下标 0 和 2 包含 '1',贡献的分数为 nums[0] + nums[2] = 2 + 5 = 7。这是可以获得的最大分数。
题目来自力扣3781。
解题过程详细解析
先明确核心规则:
- 初始分数:所有
s中为1的位置,直接加对应nums值; - 允许操作:只能交换相邻的
0和1(要求左边是0、右边是1),可以交换任意次; - 本质:
1可以向左移动到任意0的位置(因为多次相邻交换能让1持续左移),我们的目标是:让1停在数值最大的位置上,最大化总分。
输入示例:
nums = [2, 1, 5, 2, 3],s = "0 1 0 1 0"(下标0~4)
初始s中1的位置:下标1、下标3。
一、整体解题思路
我们从右向左遍历数组(从最后一个元素往第一个元素走),配合最小堆实现最优选择:
- 最小堆的作用:存储当前已选中的1对应的数值,堆顶永远是最小的那个数;
- 遍历规则:
- 遇到
s[i]='1':必须选这个位置,数值加入总分,同时放入最小堆; - 遇到
s[i]='0':这个位置可以放一个1(因为1能左移过来),如果当前位置的数值 > 堆里最小的数,就替换:用更大的数替换堆里最小的数,总分也同步更新(只加差值);
- 遇到
- 最终堆里保留的就是k个最大的数(k是原字符串中1的个数),总和就是最大分数。
二、分步骤详细过程(对应示例遍历)
原数组:下标0(2)、下标1(1)、下标2(5)、下标3(2)、下标4(3) 原字符串:0、1、0、1、0 原1的数量:2个(最终必须选2个位置放1) 遍历方向:从下标4 → 下标0
步骤1:遍历下标4(数值3,s='0')
- 当前堆为空,没有可以替换的数,不做任何操作。
步骤2:遍历下标3(数值2,s='1')
- 这是必须选的1,总分 +=2(当前总分=2);
- 把数值2放入最小堆,堆:
[2](堆顶是2)。
步骤3:遍历下标2(数值5,s='0')
- 这是0的位置,可以放1;
- 比较:当前数5 > 堆顶最小值2;
- 执行替换:总分 += 5-2 =3(总分=2+3=5);
- 用5替换堆顶的2,堆调整为
[5](堆顶是5)。
步骤4:遍历下标1(数值1,s='1')
- 这是必须选的1,总分 +=1(当前总分=5+1=6);
- 把数值1放入最小堆,堆:
[1,5](堆顶是最小的1)。
步骤5:遍历下标0(数值2,s='0')
- 这是0的位置,可以放1;
- 比较:当前数2 > 堆顶最小值1;
- 执行替换:总分 +=2-1=1(总分=6+1=7);
- 用2替换堆顶的1,堆调整为
[2,5](堆顶是2)。
三、最终结果
遍历结束,总分=7,和题目示例输出完全一致。 最终选中的两个位置:下标0(2)、下标2(5),总和2+5=7。
四、复杂度分析
1. 时间复杂度
- 遍历数组:O(n)(n是数组长度,每个元素仅遍历一次);
- 堆操作:每个元素最多入堆、出堆、调整堆各一次,堆的大小最大为
k(原1的个数),单次堆操作O(logk); - 总时间复杂度:O(n log n)(logk ≤ logn,是最优可接受复杂度)。
2. 额外空间复杂度
- 仅使用了一个最小堆存储元素,堆的最大空间为
k(原1的个数); - 总额外空间复杂度:O(n)(最坏情况全是1,堆大小为n)。
总结
- 核心逻辑:从右向左遍历,用最小堆动态保留最大的k个数值(k=原1的数量);
- 操作本质:利用规则让1左移,替换掉更小的数值,实现分数最大化;
- 复杂度:时间O(n log n),空间O(n),能高效处理n≤1e5的大数据量。
Go完整代码如下:
package main
import (
"container/heap"
"fmt"
"sort"
)
func maximumScore(nums []int, s string) (ans int64) {
h := hp{}
// Traverse from the end to the beginning
for i := len(nums) - 1; i >= 0; i-- {
x := nums[i]
if s[i] == '1' {
ans += int64(x)
heap.Push(&h, x)
} else if h.Len() > 0 && x > h.IntSlice[0] {
ans += int64(x - h.IntSlice[0])
h.IntSlice[0] = x
heap.Fix(&h, 0)
}
}
return
}
type hp struct{ sort.IntSlice }
func (h *hp) Push(v any) { h.IntSlice = append(h.IntSlice, v.(int)) }
func (hp) Pop() (_ any) { return }
func main() {
nums := []int{2, 1, 5, 2, 3}
s := "01010"
result := maximumScore(nums, s)
fmt.Println(result)
}
Python完整代码如下:
# -*-coding:utf-8-*-
import heapq
def maximumScore(nums, s):
ans = 0
h = [] # min heap
# Traverse from the end to the beginning
for i in range(len(nums) - 1, -1, -1):
x = nums[i]
if s[i] == '1':
ans += x
heapq.heappush(h, x)
elif h and x > h[0]:
ans += x - h[0]
heapq.heapreplace(h, x) # pop smallest and push x
return ans
def main():
nums = [2, 1, 5, 2, 3]
s = "01010"
result = maximumScore(nums, s)
print(result)
if __name__ == "__main__":
main()
C++完整代码如下:
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;
long long maximumScore(vector<int>& nums, string s) {
long long ans = 0;
// Min heap using greater<int>
priority_queue<int, vector<int>, greater<int>> pq;
// Traverse from the end to the beginning
for (int i = nums.size() - 1; i >= 0; i--) {
int x = nums[i];
if (s[i] == '1') {
ans += x;
pq.push(x);
} else if (!pq.empty() && x > pq.top()) {
ans += x - pq.top();
pq.pop();
pq.push(x);
}
}
return ans;
}
int main() {
vector<int> nums = {2, 1, 5, 2, 3};
string s = "01010";
long long result = maximumScore(nums, s);
cout << result << endl;
return 0;
}