LeetCode 3694. 刪除子字串後不同的終點(定長滑動視窗圖解)
3694. 刪除子字串後不同的終點
題目描述
給你一個由字元 'U'、'D'、'L' 和 'R' 組成的字串 s,表示在無限的二維笛卡爾網格上的移動。
'U':從(x, y)移動到(x, y + 1)。'D':從(x, y)移動到(x, y - 1)。'L':從(x, y)移動到(x - 1, y)。'R':從(x, y)移動到(x + 1, y)。
你還得到了一個正整數 k。
你必須選擇並移除恰好一個長度為 k 的連續子字串 s。然後,從座標 (0, 0) 開始,按順序執行剩餘的移動。
回傳可到達的不同最終座標的數量。
範例 1
1 | |
範例 2
1 | |
範例 3
1 | |
限制
1 <= s.length <= 10^5s只包含'U'、'D'、'L'和'R'。1 <= k <= s.length
本文題解思路與程式碼整理自 LeetCode 題解作者「灵茶山艾府」,題解連結。圖解與步驟說明為自行補充。
前置知識:定長滑動視窗
定長視窗每次右移一格,少一個最左元素、多一個新元素,所以視窗內的總和可以用「加新的、減舊的」在 O(1) 內更新。這題的「和」不是一個數字,而是一個二維位移 (x, y),做法一樣。前面幾篇已經用過,例如 2841 和 3439。
思路:終點的差別,只取決於被刪掉的那段位移
第一步:刪掉一段,相當於終點減去這段的位移
設不移除任何子字串時,最終座標是 (x, y)。
如果我們刪掉了子字串 "UR",就是少走了一次「y 加一」和一次「x 加一」,所以最終座標是 (x − 1, y − 1)。
換個角度,站在最終座標的位置看,刪掉子字串後的偏移量是 (−1, −1)。
這個關係對每一種移除方式都成立:
最終座標 = 不刪除時的終點
(x, y)− 被刪掉那段的位移
第二步:不同終點 = 不同位移
(x, y) 是固定的,所以兩種移除方式的最終座標相不相同,完全取決於被刪掉那段的位移相不相同。問題因此變成:
對
s中所有長為k的子字串,算出它們的位移(dx, dy),數有多少個不同的位移。
下面用 s = "UDRUR"、k = 2 實際走一遍。紅框是被移除的子字串,格子是座標平面(O 是起點),藍色圓點是這種移除方式的終點,綠色圈圈是之前已經出現過的終點,虛線框是不移除任何東西時的終點 (2, 1)。按「下一步」,注意最後兩種移除方式 "RU" 和 "UR":順序不同,但位移都是 (1, 1),所以終點相同,只算一個。
第三步:用定長滑動視窗算所有長為 k 的子字串位移
「所有長為 k 的子字串的位移」就是標準的定長滑動視窗:
- 入:
s[i]進入視窗,位移加上s[i]的方向。 - 若視窗還沒滿(長度不足
k),繼續。 - 更新:把目前的位移
(x, y)放進雜湊集合。 - 出:
s[i − k + 1]離開視窗,位移減去它的方向,為下一個視窗做準備。
最後集合的大小就是答案。因為 (x, y) 是一對整數,Java 可以把它壓成一個 long 當作集合的元素,例如 (long) (x + n) << 20 | (y + n)(+ n 避免負數)。
兩種寫法的差別只在於要不要「真的算出第一個視窗的位移」:
- 優化前:照模板算每一個視窗的位移。
- 優化後:因為終點只和位移的差有關,所以只算「後續視窗相對第一個視窗的增減量」,第一個視窗直接當
(0, 0)。
方法一:優化前(直接算每個視窗的位移)
思路
- 方向表
DIRS:L → (−1, 0)、R → (1, 0)、D → (0, −1)、U → (0, 1)。 i往右走,s[i]進入視窗:x += DIRS[c][0]、y += DIRS[c][1]。- 若視窗還沒滿(
left = i + 1 − k < 0),繼續。 - 把目前的位移放進集合
set。 s[left]離開視窗:x、y各自減去它的方向。
圖解(可互動)
用 s = "UDRUR"、k = 2。視窗位移依序是 "UD" → (0, 0)、"DR" → (1, −1)、"RU" → (1, 1)、"UR" → (1, 1),最後一個重複,集合大小是 3。
解法(灵茶山艾府)
1 | |
複雜度分析
- 時間複雜度:O(n),其中 n 是
s的長度。 - 空間複雜度:O(n − k)。有
n − k + 1個視窗,所以雜湊表中至多保存n − k + 1個不同座標。
方法二:優化(只算相對第一個視窗的增減量)
思路
所有視窗都可以由第一個視窗推出來,所以只需要算相對第一個視窗的增減量:
- 增減量不同,位移就不同,最終座標也就不同。
- 因此不需要算第一個視窗的位移,直接把它的增減量記為
(0, 0)。 - 後續視窗右移一格時,增減量
+= 新進入字元的方向 − 離開字元的方向,把它放進集合。
這樣迴圈從 i = k 開始,省掉了初始累加和「視窗還沒滿」的判斷。
圖解(可互動)
同樣用 s = "UDRUR"、k = 2。注意這裡看到的 (x, y) 是相對第一個視窗的差,和方法一的位移相差一個固定的量,但「是否重複」的結果完全一樣,集合大小同樣是 3。
解法(灵茶山艾府)
1 | |
這裡 set.add((long) n << 20 | n) 就是把 (0, 0) 用同樣的壓縮方式(+ n)放進集合。
複雜度分析
- 時間複雜度:O(n − k),其中 n 是
s的長度。 - 空間複雜度:O(n − k)。有
n − k + 1個視窗,所以雜湊表中至多保存n − k + 1個不同座標。