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

108 阅读3分钟

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

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

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

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

示例 1:

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

示例 2:

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

题解 :

/**
 * @param {number[]} hours
 * @return {number}
 */
// 解题思路
// 方法一:尝试用前缀和的方法来解决 O(n2)
// 1、我们把时间数组用1和 - 1来统计,大于8的记为1,否则记为 - 1。
// 原时间数组[9, 9, 6, 0, 6, 6, 9]则变为 newList = [1, 1, -1, -1, -1, -1, 1]。
// newList中,一个时间段内的元素加起来 > 0则这个时间段就是良好时间段,
// 比如[1, 1, -1]所有元素和大于0,则是良好时间段

// 2, 综上,问题简化为求[1, 1, -1, -1, -1, -1, 1] 中,所有元素和大于0的序列中,
// 长度最长的那个序列长度。到此,其实可以用暴力解题的方法解答了。
// 设任意一个下标从i到j的序列段为[i, j], 只需要双重遍历找到所有[i, j]序列和大于0的序列段,
// 再比较大小就能得出结果

// 3, 这里,为了减少时间复杂度,我们优化下,用前缀和的方式来解题。
// 前缀和,为每个项的值都为前面所有项的和,再加上自己。
// 以上newList序列转化为前缀和为 addList = [0, 1, 2, 1, 0, -1, -2, -1]。
// 注意这里最前面加了个0是为了比较第一天的情况,i = 0,j = 1则可表示第一天的情况。
// 我们要求原本newList[i, j]之间元素的和就很简单了,
// 在addList中直接用addList数组j项的值减i项的值即可。

var longestWPI = function (hours) {
    let addList = [0];// 前缀和数组,初始为 0
    // 一边遍历一边获得数组
    for (let i = 0; i < hours.length; i++) {
        // 判断大于8则,在上一项的基础上 + 1,否则就 - 1
        addList.push(hours[i] > 8 ? addList[i] + 1 : addList[i] - 1);
    }
    // 现在就能得到前缀和数组addList
    console.log(addList);// [0, 1, 2, 1, 0, -1, -2, -1] 与预期相同
    // 双重遍历addList里所有i到j的差,找出满足addList[j] - addList[i] > 0,
    // 且j - i跨度最大的,就是最长的表现良好时间
    let result = 0;
    let n = addList.length;
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            if (j > i && addList[j] - addList[i] > 0) {
                // 判断满足良好时间段,再和现有的良好时间段长度比较,取大值
                result = Math.max(result, j - i);
            }

        }
    }
    return result;
};

// 方法二:使用前缀和 + 单调栈 O(n)
// 定义一个前缀和数组,代表当前天以前所有工作时长 > 8小时的天数;
// 求最长时间段,就是求上面前缀和数组前后元素满足关系 i < j && preSum[i] < preSum[j] 的最大跨度问题
// 求解方法:定义一个单调递减栈,保存从左到右出现的最小值的最左边下标;
// 然后从右到左查找元素 > 单减栈栈顶,更新最大跨度;
// 以[9, 9, 6, 0, 6, 6, 9]为例
var longestWPI = function (hours) {
    // 前缀和 => preSum = [0, 1, 2, 1, 0, -1, -2, -1]
    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
    }

    // 单减栈 => stack = [0, 5, 6]
    //  preSum = [0, 1, 2, 1, 0, -1, -2, -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--) {
        while (stack.length > 0 && preSum[stack[stack.length - 1]] < preSum[i]) {
            max = Math.max(max, i - stack.pop())
        }
    }
    return max
};

 

来源:力扣(LeetCode)

链接:leetcode.cn/problems/lo…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。