問題:
如何用Python實現給定整數序列中尋找最小長度窗口以包含所有不同元素的算法?
核心思路
核心思路是利用雙端隊列(作為滑動窗口)來找到一個滿足特定條件的最小長度子序列。算法遍歷給定的序列,對于每個新數據點,執行以下三種操作之一:
- 如果數據點不在當前窗口中,則將其添加到隊列中(表示窗口擴展)。
- 如果數據點已存在于窗口中,但隊首和隊尾的元素不同(意味著窗口中尚需包含此元素),則仍然將其加入隊列。(損耗元素,知道有但是還是得加入)
- 如果數據點已存在,且隊首和隊尾的元素相同(表示該元素類型已滿足),則從隊列中移除隊首元素(縮短窗口)。
在遍歷過程中,維護一個記錄最小長度窗口的變量。如果當前窗口包含所有必需的不同元素類型,并且比之前記錄的任何窗口都小,則更新最小長度窗口的記錄。最終,得到一個滿足條件的最小長度窗口。
進一步給出偽碼思路
初始化一個雙端隊列 q 和一些計數器以及變量。定義主函數 main:讀取輸入的 n 和 m,分別代表數組長度和目標不同元素的數量。讀取數組 a 并將其轉換為整數列表。對數組 a 中的每個元素執行以下操作:如果當前元素的計數器 cnt 為 0,增加類型數量 type。增加當前元素的計數器。將當前索引添加到雙端隊列 q 的末尾。如果雙端隊列 q 不為空且隊首元素的計數器大于 1,移除隊首元素,直到計數器為 1。如果當前類型數量等于目標 m 且雙端隊列的長度小于當前記錄的最小長度 ans,則更新 ans 以及起始索引 l 和結束索引 r。打印出最小長度窗口的起始索引和結束索引,索引加 1(因為 Python 索引從 0 開始)。定義函數 qsize 來返回雙端隊列 q 的大小。如果這是主程序,調用 main 函數。
CODE
from collections import deque# 初始化變量
ans = float('inf')
n, m = 0, 0
a = []
cnt = [0] * 10000
l, r, type = 0, 0, 0
q = deque()def main():global ans, l, r, typen, m = map(int, input().split())a = list(map(int, input().split()))# 統計每個數出現的次數,并判斷是否需要type++for i in range(n):if cnt[a[i]] == 0:type += 1cnt[a[i]] += 1q.append(i)# 移除隊首元素,直到cnt[a[q[0]]]為0while q and cnt[a[q[0]]] > 1:cnt[a[q.popleft()]] -= 1# 如果type=m,且隊列長度<ans,則更新ans和l,rif type == m and qsize() < ans:ans = qsize()l = q[0]r = q[-1]print(l + 1, r + 1) # 輸出時加1,因為Python索引從0開始def qsize():return len(q)if __name__ == '__main__':main()
注:
-
ans = float('inf')
:
ans
變量用于存儲找到的滿足條件的最小長度子數組的長度。初始化為正無窮大(float('inf')
),意味著我們從找一個非常大的長度開始,隨著算法的進行,如果找到更小的滿足條件的子數組,就會更新這個值。 -
n, m = 0, 0
:
n
是數組a
的長度,即數組中元素的總數。
m
是我們需要在子數組中找到的不同元素的類型數。 -
a = []
:
a
是一個空列表,稍后將用來存儲輸入的整數數組。 -
cnt = [0] * 10000
:
cnt
是一個長度為 3000 的列表,用于計數。它將用于跟蹤數組a
中每個元素的出現次數,10000因為測試用例可能比較大 -
l, r, type = 0, 0, 0
:
l
和r
是子數組的起始和結束索引,初始化為 0。隨著算法的進行,它們將被更新為滿足條件的最小長度子數組的起始和結束索引。
type
是一個計數器,用于跟蹤當前窗口中不同元素的類型數。 -
q = deque()
:
q
是一個deque
(雙端隊列)實例,它將被用作滑動窗口的數據結構。雙端隊列允許我們從兩端快速添加和刪除元素,非常適合實現滑動窗口。