LeetCode 1695. 删除子数组的最大得分

3 阅读4分钟

题目链接

1695. 删除子数组的最大得分

题目描述

给你一个正整数数组 nums,请你从中删除一个含有若干不同元素的子数组(子数组中的元素都是唯一的)。删除子数组的得分就是子数组各元素之和,返回只删除一个子数组可获得的最大得分。

示例

  • 输入:nums = [4,2,4,5,6]

    • 输出:17
    • 解释:最优子数组是 [2,4,5,6],和为 17。
  • 输入:nums = [5,2,1,2,5,2,1,2,5]

    • 输出:8
    • 解释:最优子数组是 [5,2,1][1,2,5],和为 8。

解法分析:滑动窗口法

核心思路

该解法使用滑动窗口技术来寻找最长的唯一元素子数组,并计算其和。核心思想是:

  1. 维护一个窗口,确保窗口内的所有元素都是唯一的
  2. 右指针扩展窗口,将元素加入窗口
  3. 当窗口内出现重复元素时,左指针收缩窗口,直到窗口内元素唯一
  4. 记录过程中窗口内元素和的最大值

代码实现

class Solution:
    def maximumUniqueSubarray(self, nums: List[int]) -> int:
        m = {}  # 记录元素的次数
        l = total = ans = 0  # 左指针、当前窗口和、最大得分
        for r in range(len(nums)):
            total += nums[r]  # 将当前元素加入窗口和
            m[nums[r]] = m.get(nums[r], 0) + 1  # 更新元素次数
            
            # 当当前元素计数大于1时,收缩左指针
            while m[nums[r]] > 1:
                total -= nums[l]  # 减去左指针元素
                m[nums[l]] -= 1  # 减少左指针元素次数
                l += 1  # 左指针右移
            
            ans = max(ans, total)  # 更新最大得分
        return ans

代码解析

  1. 初始化变量

    • m:字典,用于记录每个元素在窗口中的出现次数
    • l:滑动窗口的左指针,初始位置为0
    • total:当前窗口内元素的和
    • ans:记录最大得分,初始为0
  2. 遍历数组

    • 右指针r从0开始遍历数组,扩展窗口
    • total += nums[r]:将当前元素加入窗口和
    • m[nums[r]] = m.get(nums[r], 0) + 1:更新当前元素在窗口中的出现次数
  3. 调整窗口

    • m[nums[r]] > 1时,说明窗口内有重复元素
    • total -= nums[l]:将左指针处的元素从窗口和中减去
    • m[nums[l]] -= 1:减少左指针处元素的计数
    • l += 1:左指针右移,收缩窗口,直到窗口内元素唯一
  4. 更新最大得分

    • ans = max(ans, total):每次调整窗口后,更新最大得分

关键逻辑说明

  • 唯一元素窗口:通过字典m记录元素出现次数,确保窗口内元素唯一
  • 动态和计算total变量实时计算窗口内元素的和,避免重复计算
  • 滑动窗口调整:当出现重复元素时,左指针向右移动,直到窗口内元素唯一
  • 最大得分追踪:在每次窗口调整后,更新最大得分

复杂度分析

  • 时间复杂度:O(n),其中n是数组长度。每个元素最多被左右指针各访问一次。
  • 空间复杂度:O(k),其中k是窗口中不同元素的数量,最坏情况下为O(n)。

示例详解

以输入nums = [4,2,4,5,6]为例:

  1. 初始化m={}, l=0, total=0, ans=0

  2. r=0,元素4

    • total = 0 + 4 = 4
    • m[4] = 0 + 1 = 1
    • m[4] = 1 不大于1,不收缩窗口
    • ans = max(0, 4) = 4
  3. r=1,元素2

    • total = 4 + 2 = 6
    • m[2] = 0 + 1 = 1
    • m[2] = 1 不大于1,不收缩窗口
    • ans = max(4, 6) = 6
  4. r=2,元素4

    • total = 6 + 4 = 10
    • m[4] = 1 + 1 = 2
    • m[4] = 2 > 1,进入收缩:
      • total = 10 - 4 = 6
      • m[4] = 2 - 1 = 1
      • l = 0 + 1 = 1
    • ans = max(6, 6) = 6
  5. r=3,元素5

    • total = 6 + 5 = 11
    • m[5] = 0 + 1 = 1
    • m[5] = 1 不大于1,不收缩窗口
    • ans = max(6, 11) = 11
  6. r=4,元素6

    • total = 11 + 6 = 17
    • m[6] = 0 + 1 = 1
    • m[6] = 1 不大于1,不收缩窗口
    • ans = max(11, 17) = 17
  7. 最终返回17,符合示例输出。

总结

该解法利用滑动窗口技术高效地解决了寻找唯一元素子数组并计算最大和的问题。通过维护一个窗口确保元素唯一,并动态计算窗口内元素的和,避免了暴力枚举所有子数组的O(n²)复杂度。

关键技巧在于:

  1. 使用字典记录元素出现次数,确保窗口内元素唯一
  2. 动态计算窗口和,避免重复计算
  3. 左右指针协同工作,高效调整窗口大小

这种方法适用于解决各种"唯一元素子数组"问题,是滑动窗口技术的经典应用场景。