如果一道關於數對、子陣列或子字串的題目在 O(n²) 下逾時,雙指標滑動視窗模式通常就是你要找的解法。
這兩個思想是競賽程式設計者早期就該學會的最有價值技巧之一。它們看似簡單,卻能把一大類暴力解法轉化為簡潔的線性時間程式碼。在 BIAA 的競賽程式設計專案中,我們把它們放在一起講授,因為它們共享一個核心洞見:與其每次都從頭檢查每一個區間,不如讓兩個索引在資料中移動,並重複利用你已經做過的工作。
雙指標技巧到底在做什麼
雙指標技巧使用兩個索引走訪一個陣列或字串,用單次掃描取代巢狀迴圈。常見的移動模式有兩種:
- 對撞指標:一個從左端開始,一個從右端開始,兩者相向而行。這是諸如已排序陣列上的 Two Sum 或裝最多水的容器這類問題的標準做法——你比較兩個端點的值,然後決定推進哪個指標。
- 同向指標:兩者都從左往右移動,往往速度不同,一個落後於另一個。它適用於諸如原地刪除已排序陣列中的重複元素,或者劃分(partition)這類問題。
收益是實打實的:許多需要巢狀迴圈 O(n²) 的問題會降到 O(n),因為每個指標最多只穿過陣列一次。需要注意的是,對撞型雙指標通常要求資料先排序,所以一旦把排序也算進來,真正的代價往往是 O(n log n)。
滑動視窗:雙指標的變體
滑動視窗是雙指標的一種特例:兩個索引朝同一方向移動,而你真正關心的答案是它們之間的那段區域。把它想像成一隻在陣列上爬行的尺蠖:右指標擴展視窗以納入新元素,當某個條件被破壞時,左指標收縮視窗。隨著視窗滑動,你維護一個動態的彙總資訊,比如一個和或一個字元計數,而不是從頭重新計算。
有兩種值得牢記的形態:
- 定長視窗:視窗長度是給定的且保持不變。你每次滑動一步,加入新元素並移除舊元素。經典例子:任意長度為 k 的連續子陣列的最大和。
- 變長視窗:視窗為滿足某個約束而伸縮。經典例子:無重複字元的最長子字串——你不斷擴展直到遇到重複字元,然後從左側收縮。
何時選用哪種模式
有幾個可靠的訊號能幫你在賽場壓力下快速判斷:
- 諸如「數對」「已排序」「交換」之類的詞往往指向對撞型雙指標。
- 諸如「連續子陣列」「子字串」「視窗」或「至多 / 恰好 K 個」之類的詞幾乎總是意味著滑動視窗。
- 如果陣列未排序且順序無關緊要,考慮先排序,或把該技巧與前綴和搭配使用。
有一個重要的注意點:基礎的滑動視窗假設擴展視窗只會讓某個量朝一個方向變化(它單調地保持有效,或單調地保持無效)。比如,存在負數時,和會隨著視窗增大而不再單調,因此樸素的視窗可能會失效。在這些情況下,學員要學會改用前綴和結合二分搜尋或其他工具。
精通不是背下兩個範本。它是在幾秒之內辨識出一道題獎勵的是哪種結構,然後信任那次線性掃描。
在官方的 USACO 賽道上,雙指標是 Silver 級別的核心主題,而滑動視窗在 Gold 級別又會再次出現,所以盡早熟練能讓你在多個賽季中持續獲益。內化這一模式的最好辦法是刻意練習:先做一道定長視窗的題,再做一道變長視窗的題,然後做一道對撞指標的數對題,並寫下是哪個訊號告訴你該用哪種方法。
如果你的孩子已經準備好,從理解這些模式邁向在真實賽場約束下加以運用,我們的教練會為他規劃一條結構化、逐題推進的路徑,邁向奧賽級別的熟練度。歡迎了解 BIAA 的競賽程式設計專案,立即開始。