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

203 阅读1分钟

这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。

题目描述

leetcode 1124. 表现良好的最长时间段

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

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

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

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

示例

示例1:

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

示例2:

输入: hours = [6,6,6]
输出: 0

提示:

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

题目解析

通过题目描述,可以知道:

  • 工作时间 大于 8小时 ,为【劳累的一天】,也就是表现良好的;
  • 工作时间 小于 8小时 ,为【不劳累的天数】,就是表现不合格;
  • 【表现良好的时间段】:这个时间段长度范围内,【劳累的天数】严格大于【不劳累的天数】
  • 最终的目的是,返回【表现良好的时间段】的最大长度,也就是在给出的hours中,找到【表现良好的时间段】最长的一段时间段。

以 示例1 为例:
hours = [9,9,6,0,6,6,9]hours中最长的一段【表现良好的时间段】为 [9,9,6]

思路

[9,9,6,0,6,6,9], 大于8小时的,+1; 小于等于8的,-1;
按照上面说的可以把[9,9,6,0,6,6,9] 转换为 [1,1,-1,-1,-1,-1,1];
这个时候可以发现,某些时间段相加大于0的就是【表现良好的时间段】,目的是找最长的一段。
总之就是,找到最长的一段时间段,让它们相加之和大于0,就是我们想要的。\

求连续一段?
使用前缀和,也就是前n项数字相加之和,这可以方便计算序列的区间和。

image.png

所以,把[1,1,-1,-1,-1,-1,1]转化成前缀和序列,就是[0,1,2,1,0,-1,-2,-1]

前缀和

  • 第一项 0
  • 后面每一项都是原序列前几项相加之和
  • 例如,如果要计算原序列最后两项之和的话,在前缀和序列中就是Sn - Sn-2

JavaScript实现

/**
 * @param {number[]} hours
 * @return {number}
 */
 //示例1
 // [9,9,6,0,6,6,9] => [1,1,-1,-1,-1,-1,1]
 // [1,1,-1,-1,-1,-1,1] => [0,1,2,1,0,-1,-2,-1]
 // 观察前缀和序列可以发现,序列前后值都是 +1,-1的关系
var longestWPI = function(hours) {
    // 前缀和
    let preSum = new Array(hours.length+1).fill(0)
    for (let i = 0; i < hours.length; i++) {
        if (hours[i] > 8) preSum[i+1] = preSum[i] + 1
        else preSum[i+1] = preSum[i] - 1
    }

    // 从左到右遍历,记录开始最小值下标
    let stack = []
    stack.push(0)
    for (let i = 1; i < preSum.length; i++){
        if (preSum[stack[stack.length-1]] > preSum[i]) stack.push(i)
    }
    // 从右到左遍历
    let max = 0
    for (let i = preSum.length-1; i > max; i--){
        // 比较当前前缀和是否大于stack中的最小值
        while(stack.length > 0 && preSum[stack[stack.length-1]] < preSum[i]){
            // 计算长度
            max = Math.max(max, i - stack.pop())
        }
    }
    return max
};