ETH官方钱包

前往
大廳
主題

LeetCode - 0659. Split Array into Consecutive Subsequences 解題心得

Not In My Back Yard | 2023-07-08 21:05:00 | 巴幣 0 | 人氣 151

題目連結:


題目意譯:
你被給定一整數陣列 nums,其以非遞減之順序排序。

請判斷是否可能將 nums 分成一個或多個子序列使得下列兩個條件都為真:
每一個子序列為連續遞增序列(即每個整數恰好為前一個整數 + 1)、
所有子序列的長度為 3 以上(含)。

如果你可以根據上述條件把 nums 分成若干個子序列,則回傳真(True);反之,則回傳假(False)。

一陣列的一個子序列為一個新陣列,其是從原陣列刪除若干個(可以是零個)元素且不更動剩餘元素的相對順序而得(即,[1,3,5] 為 [1,2,3,4,5] 的一個子序列;而 [1,3,2] 則不是)。

限制:
1 ≦ nums.length ≦ 10 ^ 4
-1000 ≦ nums[i] ≦ 1000
nums 以非遞減之順序排序。



範例測資:
範例 1:
輸入: nums = [1,2,3,3,4,5]
輸出: true
解釋: nums 可以被分成以下子序列:
[1,2,3,3,4,5] --> 1, 2, 3
[1,2,3,3,4,5] --> 3, 4, 5

範例 2:
輸入: nums = [1,2,3,3,4,4,5,5]
輸出: true
解釋: nums 可以被分成以下子序列:
[1,2,3,3,4,4,5,5] --> 1, 2, 3, 4, 5
[1,2,3,3,4,4,5,5] --> 3, 4, 5

範例 3:
輸入: nums = [1,2,3,4,4,5]
輸出: false
解釋: 不可能將 nums 分成若干個長度為 3 以上(含)的連續遞增子序列。


解題思維:
可以看到連續遞增的子序列中,長度可以越長越好。例如說:[1,1,2,2,3,3,4,5],直接形成 [1,2,3,4,5] 會比把它拆成兩個部分 [1,2,3] 、 [4,5] 還來得直接(而且後者還不符合規則)。



繼續上面的例子:
由於我們一開始什麼序列都還沒形成,所以很自然的我們會先挑出第一個數字。而這邊的第一個是 1,而挑了 1 自然的就要挑 2 和 3。

可以看到此時如果原數列是 [1,3,4]……之類的就表示不用繼續看了,因為挑了 1 之後就沒戲唱了。

挑出 1 、 2 、 3 之後,我們便有一個傾向去挑一個 4。根據未來視(即人眼直接看數列整體),我們確實可以挑到一個 4,因此又給了我們一個「下一個」傾向去挑 5 的「需求」。

所以可以看到我們會需要平衡某種「供需」的關係。而「供給」很直覺,就是原數列中的數字種類;至於「需求」則會根據我們先前挑的數字有所變動(如上),也就是用來接續前面的連續遞增之序列用的。



因此我們可以使用兩個雜湊表(Hash Table),一個叫 supplies(「供給」)、一個叫 demands(「需求」)來表示當前供需的關係。因此如果 supplies[7] = 2,則代表我們可以提供 2 個 7,也就是還有 2 個 7 可以挑;而 demands[8] = 3,則代表我們需要滿足 3 個 8 的需求(如果我們有得挑的話)。

接著我們先掃過一次 nums,來把 supplies 的內容建立出來。

再接著我們再次掃過 nums(也可以改掃過 supplies 中每一種數字,因此接著的操作會跟掃 nums 不大一樣)。

假設我們現在看到數字 nums[i]。先檢查 supplies[nums[i]] 是否為 0。如果是 0 則跳過這個數字(代表前面已經先把這個數字挑走了)。

接著,檢查一下 demands[nums[i]] 是否為 0。如果不是,則代表這個數字可以接在先前某個連續遞增的序列之後。而根據一開始的觀察,直接接在前一個序列的後面會比另闢蹊徑開一個新序列的開頭還來得好(因為新開不一定會成功,而延續前面一定可以,只要前面一開始就挑好長度 3 即可)。因此此時我們把 demands[nums[i]] 減去 1、supplies[nums[i]] 減去 1 並且把 demands[nums[i] + 1] 加上 1,作為延續數列的可能性(儘管 nums[i] + 1 可能不存在或是已經被挑走了)。

而如果這個數字不被需要(demands[nums[i]] = 0)且處於還可以被挑的狀態下(supplies[nums[i]] > 0),則我們就挑這個數字作為新序列的開頭。而一旦挑了 nums[i],我們就需要一併挑 nums[i] + 1 和 nums[i] + 2,否則不會成功。因此此時可以檢查 supplies[nums[i] + 1] 和 supplies[nums[i] + 2] 是否都非 0。如果是的話,就表示我們可以一次挑這三個數字,因此把 supplies[nums[i]] 、 supplies[nums[i] + 1] 、 supplies[nums[i] + 2] 通通減去 1,並將 demands[nums[i] + 3] 加上 1(如先前一致,作為延續數列用)。

而如果以上情況通通都不符合,則代表現在看到的這個數字 nums[i] 既不能作為開頭,也不能延續前面的序列。因此 nums 不可能被拆分成若干個長度 ≧ 3 的連續遞增子序列,直接回傳假即可。



最後,如果成功掃過整個 nums。則代表 nums 可以成功被拆分。因此回傳真。




此次分享到此為止,如有任何更加簡潔的想法或是有說明不清楚之地方,也煩請各位大大撥冗討論。

創作回應

更多創作