一.题目
给你一个整数数组 n u m s nums nums和一个整数 x x x 。每一次操作时,你应当移除数组 n u m s nums nums 最左边或最右边的元素,然后从 x x x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x x x 恰好减到 0 0 0,返回最小操作数 ;否则,返回 − 1 -1 −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 。
提示:
- 1 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
- 1 < = n u m s [ i ] < = 1 0 4 1 <= nums[i] <= 10^4 1<=nums[i]<=104
- 1 < = x < = 1 0 9 1 <= x <= 10^9 1<=x<=109
二.解题过程
2.1 思路分析
根据题意,我们需要在一个数组 n u m s nums nums中不断删除开头或结尾的元素,直到删去元素的和等于 x x x,而要完成这一目标,可能有很多种操作方法,我们需要寻找满足该条件最少的删除次数,举个栗子吧:
nums=[1,1,1,1,1,4,7,5,3,2],x=5
其操作方式可以为:
方式1.删除开头的5个1,即删除5次
方式2.删除末尾的3,2,即删除2次
方式3.删除开头的3个1和结尾的2,即删除4次
其中方式2才是满足条件的最少删除次数。设原数组的元素和为 s u m sum sum,由于题目中限定了每次只能删除开头或结尾的元素,因此删除后留下来的是一个原数组的连续子序列,该子序列的值为 s u m − x sum - x sum−x,其中 s u m sum sum为数组 n u m s nums nums的元素和。基于此,我们可以将原问题转化为求一个数组 n u m s nums nums中和为 s u m − x sum - x sum−x的最长连续子序列,然后我们用数组的长度减去该最长子序列的长度就可以得到原题的答案。
2.2 算法介绍
接下来,简要介绍一下如何求一个数组 n u m s nums nums中和为 s u m − x sum -x sum−x的最长连续子序列(划重点):
-
首先,设待求满足条件的连续最长子序列的长度为 r e s = 0 res = 0 res=0,然后求出 n u m s nums nums各个元素为结尾元素的子序列的和数组 t o t a l total total,即:
#n为数组长度 total[0] = nums[0] total[1] = nums[0] + nums[1] ... total[n] = nums[0] + nums[1] + ... + nums[n]
-
然后,我们从头开始遍历数组 n u m s nums nums,当遍历到位置 i ( 0 ⩽ i ⩽ n ) i(0 \leqslant i \leqslant n) i(0⩽i⩽n)时,需要寻找是否有以 i i i为结尾元素的子序列 n u m s [ j . . i ] nums[j..i] nums[j..i],其满足:
∑ m = j i n u m s [ m ] = s u m − x \sum_{m=j}^i nums[m]=sum-x m=j∑inums[m]=sum−x
若存在则将此时的序列长 i − j i-j i−j与之前记录的最长子序列 r e s res res进行比较,若 i − j > r e s i-j > res i−j>res,则 r e s = i − j res = i - j res=i−j。注:子序列 n u m s [ j . . i ] nums[j..i] nums[j..i]的和通过 t o t a l [ i ] total[i] total[i]减去 t o t a l [ j − 1 ] total[j-1] total[j−1]来获得
-
最后,用数组 n u m s nums nums的长度减去该最长子序列的长度,即为满足原提议的最小操作次数。
三.示例代码
这里给出该算法的python实现代码,需要注意的是我并没有真正去实现一个 t o t a l total total数组(后面为了表述方便继续使用这个概念),而是通过哈希表(通过字典 < k e y , v a l u e > <key,value> <key,value>的形式)来存储数组 n u m s nums nums中各个元素对应的 t o t a l [ i ] total[i] total[i]以及元素对应的位置 i i i,即 k e y = t o t a l [ i ] , v a l u e = i key = total[i],value = i key=total[i],value=i。通过这种方式,我们可以快速查询到是否有以 n u m s [ i ] nums[i] nums[i]为结束元的子序列,满足其和等于 s u m − x sum - x sum−x,方法是寻找是否存在键为 t o t a l [ i ] − ( s u m − x ) total[i] - (sum -x ) total[i]−(sum−x)的元素,这样以来就可以避免了双重循环(肯定超时)。
from typing import List
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
#初始化满足条件的最长子序列长res为0
s,res = 0,0
#计算sum -x
k = sum(nums) - x
#初始化字典
map = {}
map[0]=-1
for i in range(len(nums)):
#累加计算total[i]
s += nums[i]
#寻找是否存在键为total[i] - (sum -x)的元素
if map.get(s - k) != None:
res = max(i - map.get(s-k),res)
#存储<total[i],i>
if map.get(s) == None:
map[s] = i
#排除nums数组和为x的情况,这种情况下,res的值也为0
if sum(nums) == x:
return len(nums)
return len(nums) - res if res != 0 else -1
运行结果截图如下:
以上便是本文的全部内容,若是觉得不错话可以点个赞或关注一下博主吧,你们的支持是博主继续创建的不竭动力!