一、 前言
- 求区间和问题转换成求相应前缀和然后做差
- 最大区间,可以用贪心,左边界越小越好,右边界越大越好
二、题目描述
原谅我比较懒,我直接从leetcode截图,欲知更加清楚的描述,请看leetcode题目链接
三、思路
- 首先入参是每天的工作时间,我们并不关注具体时间,只关注这一天是否是劳累的一天,大于8小时的为劳累的一天记作1,小于8小时的为不劳累的一天记作-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] - 贪心,区间求和,左边界越小越好,右边界越大越好
- 从左往右扫描前缀和数组,初始化一个栈,存前缀和数组中符合要求的下标。
- 这个要求就是左边界越小越好,故存前缀和数组中依次递减的元素的下标(注意这里不是单调递减栈,因为其存的是下标,是单调递增的)
- 从右往左扫描前缀和数组,右边界越大越好。找出比栈中元素大的出栈,此时为最右侧。求差值然后与已记录得最大区间值比较,得最大区间值。
四、代码
/**
* @param {number[]} hours
* @return {number}
*/
const longestWPI = function(hours) {
// 这里直接求前缀和数组,好处是少遍历一次,坏处是不好理解了。其实就是求映射和前缀和这两步的合并
const preSum = new Array(hours.length + 1).fill(0);
for(let i = 0; i < hours.length; ++i) {
preSum[i+1] = hours[i] > 8 ? preSum[i] + 1 : preSum[i] - 1;
}
// 前缀和中依次递减元素的下标栈, 初始化最小元素是第0项
const indexStack = [0]
// 从左往右扫描前缀和数组, 存前缀和中符合要求元素的下标,由于已经初始化过,故从索引为1的元素往后扫描
for(let i = 1; i < preSum.length; ++i) {
if(preSum[i] < preSum[indexStack[indexStack.length - 1]]) indexStack.push(i)
}
// 区间最大结果,初始化为0
let max = 0;
// 从右往左扫描,找右边界最大
for(let i = preSum.length - 1; i > max; --i) {
while(preSum[i] > preSum[indexStack[indexStack.length - 1]]) {
max = Math.max(max, i - indexStack.pop())
}
}
return max
}