[路飞]_leetcode-1124-表现良好的最长时间段

277 阅读2分钟

一、题目描述

给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。

我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。

所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。

请你返回「表现良好时间段」的最大长度。

示例 1:

输入: hours = [9,9,6,0,6,6,9]
输出: 3
解释: 最长的表现良好时间段是 [9,9,6]

提示:

  • 1 <= hours.length <= 10000
  • 0 <= hours[i] <= 16

本题定义表现良好的时间段为劳累天数大于不劳累天数的区间,那么最朴素的解题方法就是

  1. 获取给定时间表的所有子区间
  2. 判断该区间是否为表现良好时间段
  3. 求得所有表现良好时间段的最大值

但是上面这种方法太低效了,那么本题除了这种方法,还有什么样的更优解呢?

分析

因为每一天只有两种可能,要么是劳累的一天,要么是非劳累的一天,又因为我们获取的时间段应该是劳累天数大于非劳累天数

所以首先我们可以将 hours 中的元素根据是否 >8 转为 1-1,以示例1为例, 转换后结果如下图:

image.png

那我们这一步操作有什么用呢,是为了求前缀和数组做准备

这里的前缀和就是每一项等于之前所有项的和值

那我们将以上结果求的前缀和后的结果如下:

image.png

前缀和的数组的第一位初始化为 0,方便后续的运算,所以之前数组第 i 项的前缀和的值对应前缀和数组的第 i+1

有了前缀和数组之后,我们用后面一项减去前面一项,所得到的值就是该区间中工作天数的累加结果

举个例子: 如上图前缀和数组

下标 2 减去下标 0 的结果为 2 ,说明前两天中,劳累天数比非劳累天数多两天

下标 4 减去下标 1 的结果为 -1 ,说明第二天到第四天中,劳累天数比非劳累天数少一天

这样用前缀和中的值减去它前面的最小值,就是该位置最好的表现结果。

代码实现

var longestWPI = function(hours) {
  // 初始化前缀和数组
  const preSum = [0],
  // 获取工作天数
  len = hours.length;
  // 获取前缀和数组
  for(let i = 0;i<len;i++){
    preSum[i+1] = preSum[i]+(hours[i]>8?1:-1)
  }
  // 获取单调递减栈
  const stack = [0];
  for(let i = 1;i<=len;i++){
    if(preSum[i]<preSum[stack[stack.length-1]])
      stack.push(i)
  }
  // 求得表现良好的最长时间段
  let res = 0;
  for(let i = len;i>res;i--){
    while(preSum[i]>preSum[stack[stack.length-1]]){
      res = Math.max(res,i-stack.pop())
    }
  }
  return res;
};

image.png 欢迎建议讨论