題目描述
給定一個小寫英文字串 s 和一個二維陣列 shifts,其中 shifts[i] = [start, end, direction]:
- 若
direction == 1,將 s[start] 到 s[end] 的所有字元向後移動一位(z → a 循環)
- 若
direction == 0,將 s[start] 到 s[end] 的所有字元向前移動一位(a → z 循環)
依序應用所有 shifts 後,回傳最終字串。
範例:s = "abc", shifts = [[0,1,0],[1,2,1],[0,2,1]]
- [0,1,0]:s = "zac"(a→z, b→a)
- [1,2,1]:s = "zbd"(a→b, c→d)
- [0,2,1]:s = "ace"(z→a, b→c, d→e)
→ 答案:"ace"
解題思路
差分陣列 + 前綴和
暴力解法:對每個操作 [start, end, direction],逐一修改 s[start] 到 s[end] 的字元,時間 O(n × k),k 為操作數,效率差。
核心思路:用差分陣列批量記錄移位量,最後一次性計算每個位置的總移位量。
差分陣列:
diff[i]:在位置 i 開始新增的移位量(相對於前一位置的變化量)
- 對操作
[start, end, direction](direction=1 表示 +1,direction=0 表示 -1):
diff[start] += d(d = 1 或 -1)
diff[end+1] -= d
對 diff 做前綴和,即得每個位置的總移位量 shift[i]。
對每個字元應用移位:
s[i] = (s[i] - 'a' + shift[i] % 26 + 26) % 26 + 'a'
加 26 確保結果非負(shift 可能為負數),再 mod 26 取循環餘數。
時間複雜度:O(n + k),其中 n 為字串長度,k 為操作次數。
替代方案
方案一:暴力逐字元更新
對每個操作 [start, end, direction],用迴圈對 s[start] 到 s[end] 的每個字元執行移位。
- 優點:邏輯最直觀,無需額外資料結構。
- 缺點:時間 O(n × k),當 n=5×10^4 且 k=5×10^4 時需 2.5×10^9 操作,完全超時。
方案二:排序事件點(掃描線)
將每個操作拆成「在 start 增加 d」和「在 end+1 減少 d」兩個事件,排序後用掃描線(sweep line)從左到右累計移位量。
- 優點:概念上與差分陣列相同,且在操作範圍不規則(如操作非常稀疏)時更直觀。
- 缺點:排序事件點需要 O(k log k) 時間,而差分陣列直接索引插入是 O(k),且差分陣列在 n 確定的情況下不需要排序,更簡單直接。
方案三:分段樹(Segment Tree with Lazy Propagation)
用懶惰線段樹支援區間加法(每次操作 O(log n))和點查詢(O(log n))。
- 優點:支援在線查詢(操作過程中隨時查詢字元),適合動態場景。
- 缺點:對本題(所有操作批量完成後一次輸出)屬於過度設計;差分陣列 O(n + k) 整體更優,程式碼也更簡單。