【LeetCode 1793 】好子数组的最高分数

题目描述

原题链接:LeetCode.1793 好子数组的最高分数

解题思路

  • 分数由子数组最小值和长度决定, 而且确定了子数组必须包含nums[k]的值;
  • 从k位置分别向左向右找更小值就是每个子数组的最小值, 左右两端边界下标也就确认了子数组长度;
  • 直观朴素的解法是利用单调栈将向左向右分别严格递减的下标保存起来, 利用栈顶下标对应的最小值和长度依次计算分数;
  • 在上述版本进一步优化代码可以直接去除单调栈, 每次先由上一轮左右边界值确定下一个子数组最小值再扩充至最大长度来计算分数。

解题代码

单调栈保存两端所有可能的最小值下标进行求解:

  /**
   * 双指针单调栈解法,类似于接雨水问题
   * 执行用时: 3 ms , 在所有 Java 提交中击败了 68.92% 的用户
   * 内存消耗: 57.47 MB , 在所有 Java 提交中击败了 9.46% 的用户
   */
  public int maximumScore(int[] nums, int k) {
      int n = nums.length, ans = 0;
      int[] indexes = new int[n];
      indexes[k] = k;
      int leftIdx = k, rightIdx = k;
      // [leftIdx...k]范围保存nums数组中[0...k]范围内严格递增的下标
      for (int i = k - 1; i >= 0; i--) {
          if (nums[i] < nums[indexes[leftIdx]]) {
              indexes[--leftIdx] = i;
          }
      }
      // [k...rightIdx]范围保存nums数组中[k...n)范围内严格递减的下标
      for (int i = k + 1; i < n; i++) {
          if (nums[i] < nums[indexes[rightIdx]]) {
              indexes[++rightIdx] = i;
          }
      }
      // 当前最小值对应的子数组最左和最右边界下标
      int leftBound = 0, rightBound = n - 1;
      while (leftIdx <= k && rightIdx >= leftIdx) {
          if (nums[indexes[leftIdx]] < nums[indexes[rightIdx]]) {
              ans = Math.max(ans, nums[indexes[leftIdx]] * (rightBound - leftBound + 1));
              leftBound = indexes[leftIdx++] + 1;
          }
          else {
              ans = Math.max(ans, nums[indexes[rightIdx]] * (rightBound - leftBound + 1));
              rightBound = indexes[rightIdx--] - 1;
          }
      }
      return ans;
  }

直接双指针确定子数组最左最右边界求解:

  /**
   * 双指针求解
   * 执行用时: 2 ms , 在所有 Java 提交中击败了 100.00% 的用户
   * 内存消耗: 56.03 MB , 在所有 Java 提交中击败了 56.76% 的用户
   */
  public int maximumScore(int[] nums, int k) {
      int ans = nums[k], n = nums.length;
      int i = k, j = k, min = nums[k];
      while (true) {
          while (i >= 0 && nums[i] >= min) i--;
          while (j < n && nums[j] >= min) j++;
          // (i, j)开区间范围内的最小值为min, 元素数量为 j-i-1
          ans = Math.max(ans, min * (j - i - 1));
          // 降低下一个子数组最小值并扩充边界
          if (i >= 0 && j < n) {
              // 左右两端都可能扩充, 下一个最小值是当前边界值中的较大值
              min = Math.max(nums[i], nums[j]);
          }
          else if (i >= 0) {
              min = nums[i];
          }
          else if (j < n) {
              min = nums[j];
          }
          else {
              break;
          }
      }
      return ans;
  }

posted on 2024-03-19 13:17  真不如烂笔头  阅读(9)  评论(0编辑  收藏  举报

导航