给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
示例 1:
输入:hours = [9, 9, 6, 0, 6, 6, 9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]。
这个思路是看的解题思路里面一个Python3大佬的答案写的。
https://leetcode-cn.com/problems/longest-well-performing-interval/solution/
大佬就是大佬,即使给出了思路,我还是看了好久好久才看懂,期间还去补习了单调栈和前缀和的知识才看懂思路。
不了解或者不够熟悉推荐再去补一下相关知识:
单调栈和应用实践
前缀和
首先根据时间是否大于8,量化成1和-1利与计算, [9, 9, 6, 0, 6, 6, 9] => [1, 1, -1, -1, -1, -1, 1],所以hours当前是[1, 1, -1, -1, -1, -1, 1]。
这个操作比较好理解,其实理解题意,我们要做的就是得到一个区间,这个区间里1和-1加起来要大于0。怎么方便的得到一个区间的和?很明显,这就是前缀和存在的意义。
转化成前缀和prefixSrc = [0, 1, 2, 1, 0, -1, -2, -1],我们在前缀和前面加了一个0,是为了好操作。那如何得到一个区间呢?如果要得到[1,3]区间,根据前缀和定义需要用prefixSrc[3]-prefixSrc[1],所以我们需要得到数组中所有prefixSrc[b]-prefixSrc[a] > 0的[a,b]区间,就是我们需要的结果。
上图所示,我们需要的就是那两段红色线段。我们选择从后往前遍历,
如何得到第二段红色线段呢?
index=7,prefixSrc[7]=-1;需要找到左边第一个比自己小的数字。这段描述熟悉不?到了单调栈登场的时候了。
我们对prefixSrc = [0, 1, 2, 1, 0, -1, -2, -1]生成单调栈,stack=[0,5,6],对应[0, -1, -2],如果当前元素大于栈顶元素,栈顶元素出栈,因为-1 > -2,所以6出栈,计算宽度width=7-6=1。
如何得到第一段红色线段呢?
其实因为我们便利到index=3的时候prefixSrc[3]的时候发现prefixSrc[3]大于0,此时栈内只剩一个0,所以宽度width=3-0=3
因为0永远在第0位所以只要出现大于0的情况,0~n就是表现良好时间段成立的。
我们继续举个例子:
OA段跟BCD段都是我们需要的,而AB段则不是。想明白这个就明白了。
- DCB:从D往C遍历的过程中我们会不断把单调栈里面的index弹出去,直到B,得到DCB的宽度。
- AO:到A后发现大于零,弹出栈底的0,得到OA的宽度
代码实现:
/**
* 使用前缀和+单调栈
*
* @param src 源数组
*/
public int longestWPI(int[] hours) {
int max = 0;
Stack<Integer> stack = new Stack<>();
int[] prefixSrc = new int[hours.length + 1];
//大于8的置为1,否则置为-1
for (int i = 0; i < hours.length; i++) {
if (hours[i] > 8) {
max = 1;
hours[i] = 1;
} else {
hours[i] = -1;
}
//初始化前缀和数组
prefixSrc[0] = 0;
prefixSrc[i + 1] = prefixSrc[i] + hours[i];
}
for (int i = 0; i < prefixSrc.length - 1; i++) {
//实现单调栈
if (stack.isEmpty()) {
stack.push(i);
} else {
if (prefixSrc[i] < prefixSrc[stack.peek()]) {
stack.push(i);
}
}
}
//开始寻找,从后往前遍历
for (int i = prefixSrc.length - 1; i >= 0; i--) {
int last = i;
while (!stack.isEmpty() && prefixSrc[i] > prefixSrc[stack.peek()]) {
last = stack.pop();
}
if (last != i) {
int width = i - last;
max = max > width ? max : width;
}
}
return max;
}