「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」
题目
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。
示例1
输入: nums = [1,1,4,2,3], x = 5
输出: 2
解释: 最佳解决方案是移除后两个元素,将 x 减到 0 。
示例2
输入: nums = [5,6,7,8,9], x = 4
输出: -1
示例3
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
题解
前缀和 + 哈希表记录长度
每次只能移除数组最左或者最右;最容易想到的是通过前缀和;左右前缀和相加等于x,将此时左右前缀长度记录下来取最小值
分析示例:nums = [3,2,20,1,1,3], x = 10
- 左侧前缀和 left = [3,5,25,26,27,30]
- 右侧前缀和 right = [30,27,25,5,4,3]
- 从left左侧,right从右侧逐一搜索查找,
- 理论上没什么问题,但是逐一搜索,时间复杂度太高,可以将前缀和放在leftMap、rightMap中,
- map中记录这个得到这个前缀和需要的长度
- 在搜索时,可以通过, k 在rightMap是否存在降低时间复杂度
- 结果取从left搜索得到最小长度与从right搜索得到的最小长度的较小值
- 注意边界处理,
根据上述思路编辑代码如下
代码
var minOperations = function (nums, x) {
const len = nums.length
const leftMap = {}
const rightMap = {}
let left = 0
let right = 0
for (let i = 0; i < len; i++) {
left += nums[i]
if (leftMap[left] === undefined) {
leftMap[left] = i + 1
}
right += nums[len - 1 - i]
if (rightMap[right] === undefined) {
rightMap[right] = i + 1
}
}
let leftNum = 0
let leftMin = len+1
for (let i = 0; i < len; i++) {
leftNum += nums[i]
const diff = x - leftNum
if (diff === 0) {
leftMin = Math.min(leftMap[leftNum], leftMin)
}
if (diff > 0 && rightMap[diff] !== undefined) {
if (leftMap[leftNum] + rightMap[diff] <= len) {
leftMin = Math.min(leftMap[leftNum] + rightMap[diff], leftMin)
}
}
}
let rightNum = 0
let rightMin = len+1
for (let i = len - 1; i >= 0; i--) {
rightNum += nums[i]
const diff = x - rightNum
if (diff === 0) {
rightMin = Math.min(rightMin, rightMap[rightNum])
}
if (diff > 0 && leftMap[diff] !== undefined) {
if (rightMap[rightNum] + leftMap[diff] <= len) {
rightMin = Math.min(rightMin, rightMap[rightNum] + leftMap[diff])
}
}
}
if (rightMin === len+1 && leftMin === len+1) return -1
if (rightMin === len+1) return leftMin
if (leftMin === len+1) return rightMin
return Math.min(leftMin, rightMin)
}
滑动窗口
对于数组
如果数组 总和为
一定有
等价于
现在将问题转换为求 数组连续子数组之和等于 的最大长度
根据上述思路编辑代码如下:
代码
var minOperations = function (nums, x) {
const len = nums.length
const total = nums.reduce((a, b) => a + b)
//console.log('total', total)
if (total < x) return -1
if (total === x) return len
const target = total - x
let current = 0
let max = 0
let result = 0
for (let i = 0; i < len; i++) {
max += 1
current += nums[i]
while (current > target) {
current -= nums[i + 1 - max]
max -= 1
}
if (current === target) {
result = Math.max(result, max)
}
}
return result === 0 ? -1 : len - result
}