前端工程師的 LeetCode 之旅 -- 周賽 182

descire發表於2020-04-05



01
找出陣列中的幸運數



題目描述【Easy】


在整數陣列中,如果一個整數的出現頻次和它的數值大小相等,我們就稱這個整數為「幸運數」。
給你一個整數陣列 arr,請你從中找出並返回一個幸運數。
如果陣列中存在多個幸運數,只需返回 最大 的那個。
如果陣列中不含幸運數,則返回 -1 。
示例:
輸入:arr = [2, 2, 3, 4]
輸出:2
解釋:陣列中的唯一幸運數是 2,因為數值 2 出現的頻次也是 2。



本道題主要考察 JavaScript 中的 Map 資料結構的使用。

第一步:利用 Map 可以來統計每個數字出現的次數。

第二步:從 Map 中找出次數出現相同並且數值最大的數。

時間複雜度 O(n),空間複雜度 O(n)。

前端工程師的 LeetCode 之旅 -- 周賽 182




02
統計作戰單位數



題目描述【Medium】


n 名士兵站成一排。每個士兵都有一個 獨一無二 的評分 rating 。
每 3 個士兵可以組成一個作戰單位,分組規則如下:
從隊伍中選出下標分別為 i、j、k 的 3 名士兵,他們的評分分別為 rating[i]、rating[j]、rating[k]
作戰單位需滿足:rating[i] < rating[j] < rating[k] 或者 rating[i] > rating[j] > rating[k] ,其中 0 <= i < j < k < n
請你返回按上述條件可以組建的作戰單位數量。每個士兵都可以是多個作戰單位的一部分。
示例:
輸入:rating = [2, 5, 3, 4, 1]
輸出:3
解釋:可以組建三個作戰單位(2, 3, 4)(5, 3, 1)(5, 4, 1)



由於本道題的陣列長度給定的範圍非常小,嘗試了一下暴露列舉竟然過了。

時間複雜度 O(n^3)。

前端工程師的 LeetCode 之旅 -- 周賽 182

總共要求有三個士兵,那麼可以以中間的士兵為參考點,向兩邊尋找滿足要求的士兵。

這實際上就是利用雙指標技巧來降維,時間複雜度優化為 O(n^2)。

前端工程師的 LeetCode 之旅 -- 周賽 182




03
5370.設計地鐵系統



題目描述【Medium】


請你實現一個類 UndergroundSystem ,它支援以下 3 種方法:
1. checkIn(int id, string stationName, int t)
編號為 id 的乘客在 t 時刻進入地鐵站 stationName 。
一個乘客在同一時間只能在一個地鐵站進入或者離開。
2. checkOut(int id, string stationName, int t)
編號為 id 的乘客在 t 時刻離開地鐵站 stationName 。
3. getAverageTime(string startStation, string endStation)
返回從地鐵站 startStation 到地鐵站 endStation 的平均花費時間。
平均時間計算的行程包括當前為止所有從 startStation 直接到達 endStation 的行程。
呼叫 getAverageTime 時,詢問的路線至少包含一趟行程。
你可以假設所有對 checkIn 和 checkOut 的呼叫都是符合邏輯的。也就是說,如果一個顧客在 t1 時刻到達某個地鐵站,那麼他離開的時間 t2 一定滿足 t2 > t1 。所有的事件都按時間順序給出。



看完本道題的描述,大部分 coder 都會想到利用 HashMap 來統計,但是要統計哪些資訊來方便 getAverageTime 方法計算呢?

首先,對於任意一個乘客,他必然是先進再出,依次迴圈,那麼當我們拿到他出站的資訊時,只需要保留進站和出站的時間間隔即可,之前的進站和出站資訊可以刪除掉,不然再統計該乘客後續的進出站就比較麻煩了。

前端工程師的 LeetCode 之旅 -- 周賽 182

最後執行 getAverageTime 方法時,利用進站和出站資訊查詢 HashMap 計算和值即可。

空間複雜度 O(n),checkIn 和 checkOut 的時間複雜度為 O(1),getAverageTime 的時間複雜度為 O(n)。

前端工程師的 LeetCode 之旅 -- 周賽 182




04
5371.找到所有好字串



題目描述【Hard】


給你兩個長度為 n 的字串 s1 和 s2 ,以及一個字串 evil 。請你返回 好字串 的數目。
好字串 的定義為:它的長度為 n ,字典序大於等於 s1 ,字典序小於等於 s2 ,且不包含 evil 為子字串。
由於答案可能很大,請你返回答案對 10^9 + 7 取餘的結果。
示例:
輸入:n = 2, s1 = 'aa', s2 = 'da', evil = 'b'
輸出:51
解釋:總共有 25 個以 'a' 開頭的好字串:'aa','ac','ad',...,'az'。還有 25 個以 'c' 開頭的好字串:'ca','cc','cd',...,'cz'。最後,還有一個以 'd' 開頭的好字串:'da'。



本題涉及兩個知識點:

  • 數位 DP

  • KMP

首先,我們可以將 evil 這個條件忽略,如何求解 [s1, s2] 之間的所有字串?

比較容易想到的思路是:判斷上下限,暴力列舉出所有情況。

這個方法存在兩個問題:

  • 在列舉的過程中,同時處理上下限是比較複雜的

  • 暴力列舉會出現很多重複列舉的情況,效率低

對於第一個問題:可以將這個問題轉為更加簡單的形式,先求解出字典序小於等於 s1,再求解出字典序小於等於 s2,那麼結果就是 s2 - s1 + 1。

對於第二個問題:從高位開始列舉結合記憶化優化重複列舉操作,這就是數位 DP。

前端工程師的 LeetCode 之旅 -- 周賽 182

本題難點在於:不包含 evil 字串。

這就需要 KMP 演算法中的 next 陣列來幫助我們記錄當前字串是否匹配上 evil 字串。

前端工程師的 LeetCode 之旅 -- 周賽 182

那麼在數位 DP 的過程中,我們需要新增一個當前匹配上 evil 字元長度的狀態,並且在列舉的過程中利用 KMP 的 next 陣列來求解當前匹配上的字元。

前端工程師的 LeetCode 之旅 -- 周賽 182




05
往期精彩回顧





前端工程師的 LeetCode 之旅 -- 周賽 182




相關文章