We are given hours
, a list of the number of hours worked per day for a given employee.
A day is considered to be a tiring day if and only if the number of hours worked is (strictly) greater than 8
.
A well-performing interval is an interval of days for which the number of tiring days is strictly larger than the number of non-tiring days.
Return the length of the longest well-performing interval.
Example 1:
Input: hours = [9,9,6,0,6,6,9]
Output: 3
Explanation: The longest well-performing interval is [9,9,6].
Constraints:
1 <= hours.length <= 10000
0 <= hours[i] <= 16
這道題說是有一個每天工作的時長數組,若每天工作時間超過8個小時,則表示是勞累的一天。又定義了一個所謂的表現良好的區間(從資本家的角度來定義的吧?!!),即勞累的天數大於不勞累的天數,然后讓返回表現良好的區間的最大長度。亮點在於給的例子,上來就是傳說中的 996,而且最終返回的表現良好的區間正好是這個 996,非常具有諷刺意味,估計是出題者有意為之。這道題跟之前那道 Contiguous Array 的解法很類似,都是玩子數組的題。雖然博主之前說過子數組求極值的題十有八九都是用動態規划來做,但是這道題正好是一個例外。由於這里我們只關心數組中的數字是否大於8,而不關心其具體的大小,所以可以進行轉換,將大於8的數字均變為1,小於等於8的變為 -1,這樣變換的好處是,只要個子數組之和大於0了,則說明一定是一個表現良好的區間。這里用一個變量 cur,表示從開頭到當前位置的子數組之和,在遍歷數組的過程中,若大於8,則 cur 加上1,否則減去1。若此時 cur 大於0,則說明從開頭到當前位置的子數組是一個良好區間,直接將結果 res 更新為 i+1。若 cur 為非正數,說明從開頭到當前位置不是一個良好區間,但中間可能還有較小的良好區間。需要用一個 HashMap 來建立子數組之和跟其結束位置坐標之間的映射,由於子數組之和可能重復,所以我們只建立最先出現的,后面再次出現則不更新,這樣保證了其坐標最小。所以對於當前的非正數 cur,只有當其不存在映射的時候,才建立映射。此時再找一下,看 cur-1 是否存在,由於 cur-1 的映射值(若存在的話)一定是小於 cur 的,這樣二者做差,中間那段區間一定是良好區間,因為其和為1,這樣就可以用這個長度來更新結果 res 了,參見代碼如下:
解法一:
class Solution {
public:
int longestWPI(vector<int>& hours) {
int res = 0, n = hours.size(), cur = 0;
unordered_map<int, int> seen;
for (int i = 0; i < n; ++i) {
cur += hours[i] > 8 ? 1 : -1;
if (cur > 0) {
res = i + 1;
} else {
if (!seen.count(cur)) {
seen[cur] = i;
}
if (seen.count(cur - 1)) {
res = max(res, i - seen[cur - 1]);
}
}
}
return res;
}
};
這道題的提示說是讓用單調棧來做,也不是不可以,但感覺沒有上面的那種解法來的更加直接。這里是要建立原數組的累加和數組,注意還是要用到之前的小技巧,將大於8的數字變為1,否則變為 -1。然后遍歷一遍累加和數組,若棧為空,或者棧頂元素大於當前數字,則將當前數字壓入棧,注意這里我們壓入的是坐標,而不是真正的數組元素。由於累加和數字的首元素是0,而之后又只壓入更小的數字,則后面的負數均被壓入棧了。然后從末尾開始遍歷,只要當前的值大於棧頂元素,說明二者中間的區域是一個良好區間,通過坐標來求出區間長度並更新結果 res,這個核心原理跟上面的一樣,更新后將元素出棧,繼續跟下一個較大的棧頂元素對比,若還是當前的大,則繼續計算區間長度並更新 res,直到當前元素小了,則繼續往前用下一個比較,或者是當棧為空了,則停止,參見代碼如下:
解法二:
class Solution {
public:
int longestWPI(vector<int>& hours) {
int res = 0, n = hours.size();
stack<int> st;
vector<int> sums(n + 1);
for (int i = 1; i <= n; ++i) {
sums[i] = sums[i - 1] + (hours[i - 1] > 8 ? 1 : -1);
}
for (int i = 0; i <= n; ++i) {
if (st.empty() || sums[st.top()] > sums[i]) {
st.push(i);
}
}
for (int i = n; i >= 0; --i) {
while (!st.empty() && sums[st.top()] < sums[i]) {
res = max(res, i - st.top());
st.pop();
}
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1124
類似題目:
參考資料:
https://leetcode.com/problems/longest-well-performing-interval/