LeetCode Problem: Find the Most Competitive Subsequence 詳解

Description

給定一個 int 陣列以及一個正整數 k,回傳一個長度為 kthe most competitive subsequence (注意:此 subsequence 不一定要連續)。當第一個相異的數值出現在陣列一和陣列二時,擁有較小數值的陣列較 competitive,舉列來說 [1, 3, 4][1, 3, 5] competitive,因為 4 小於 5。

Example 1:

Input: nums = [3,5,2,6], k = 2
Output: [2,6]
Explanation: Among the set of every possible subsequence: {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]}, [2,6] is the most competitive.

Example 2:

Input: nums = [2,4,3,3,5,4,9,6], k = 4
Output: [2,3,3,4]

Solution

假設 ans 為將要回傳之變數,我們需要做的事情是,遍歷 nums 的元素,並且將合適的元素推入 ans 當中,而 ans 裡不合適的元素要刪除,最終得到一個長度為 k 的陣列。我們將以以下原則遍歷 nums

  1. ans 存儲的元素長度小於 k 時,無條件將遍歷之元素推入 ans。但是推入元素前,我們會先進行以下原則的檢查
  2. 當遍歷到 nums[i] 時發現:
    $\text{nums[i]} \geq \text{ans[j]}, \text{for } j \in [0, t]$
    $\text{nums[i]} < \text{ans[j]}, \text{for } j \in [t + 1, k)$ 則應該將 ans[t + 1], ..., a[k - 1] 的值全部排除後,再將 nums[i] 加入 ans
  3. 必須確保尚未遍歷的元素數量大於 ans 的空位,若剩餘的元素剛好等於空位數,則將不再進行原則 2 將元素排除的動作

對於原則1很好理解,因為我們必須確保 ans 有足夠的長度 k,因此當 ans 長度小於 k 時,必須直接將新的遍歷元素加入 ans 中。舉例來說:nums = [100, 100, 100, 100], k = 4,無論 nums[i]的值有多大,我們都必須放入 ans 當中,因為唯有如此才能確保 ans 有足夠的長度(長度為 4),因此在此例中,我們需要推入所有 nums 的元素至 ans,得到 ans = [100, 100, 100, 100]

至於原則2,我們想要確保越小的值排在 ans 越前面的位置,假設 k = 4 ,並且在我們遍歷 nums 一段時間後得到 ans = [1, 3, 4, 5],若下一個遍歷元素為 2,則我們要將 [3, 4, 5] 全部移除再將 2 推入,得到 ans = [1, 2] ,需要將 [3, 4, 5] 排除是因為就算接下來只剩下很大的元素未遍歷而放入 ans 當中,如:ans = [1, 2, 100, 100],還是會比 ans = [1, 3, 4, 5] 還要 competitive。

最後原則3,我們要確保剩下還未遍歷的值足夠填滿 ans,否則我們將不會在將 ans 的尾部元素刪除,因為若我們刪除 ans 中的任一元素,其長度將永遠達不到 k,違反題目要求。我們以以下幾個例子闡述整個算法。

完整算法如下(in C++)

class Solution {
public:
    vector<int> mostCompetitive(vector<int>& nums, int k) {
        vector<int> ans;
        for (int idx = 0; idx < nums.size(); ++idx)
        {
            while (ans.size() > 0 && 
                   nums[idx] < ans.back() && 
                   k - ans.size() < nums.size() - idx)
            {
                ans.pop_back();
            }
            if (ans.size() < k) ans.push_back(nums[idx]);
        }

        return ans;

    }
};