Medium草稿★★★★★O(n) 時間 · O(1) 空間
StringStackGreedy
Patterns🥞 堆疊🪙 貪心
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

1963Minimum Number of Swaps to Make The String BalancedArrays & HashingMediumArrays & Hashing

給定一個長度為 2n 的字串 s,只包含 [],且各有 n 個。你可以交換任意兩個位置的字元,回傳使字串成為合法括號字串所需的最少交換次數。

Example:

Input: s = ”][]” Output: 1 (交換 index 0 和 index 3 得到 ”[[]]“)

Intuition

TIP

核心思路:消除所有已配對的括號後,剩下的一定是 ]]]...[[[ 形式。若剩餘未配對的 ]k 個,則最少交換次數為 ⌈k/2⌉

Approaches

1. Stack Simulation — O(n) / O(n)
  • Idea: 用 Stack 消除配對括號,最後 Stack 中剩餘的就是未配對的括號數
  • Time: O(n) - 遍歷一次字串
  • Space: O(n) - Stack 最壞存 n 個字元
class Solution {
    fun minSwaps(s: String): Int {
        val stack = ArrayDeque<Char>()
        for (c in s) {
            if (c == '[') {
                stack.addLast(c)
            } else {
                if (stack.isNotEmpty() && stack.last() == '[') {
                    stack.removeLast()
                } else {
                    stack.addLast(c)
                }
            }
        }
        // stack 中剩餘的是交替的 ]...[ ,未配對的 ] 數量 = stack.size / 2
        val unmatched = stack.size / 2
        return (unmatched + 1) / 2
    }
}
⭐ 2. Greedy Counter (O(1) Space) — O(n) / O(1)
  • Idea: 不用 Stack,只追蹤目前未配對的 ] 數量(close 計數器)。遇到 [ 時若有未配對 ] 就消掉,否則不管;遇到 ] 時若無 [ 可配對就累加
  • Time: O(n) - 遍歷一次字串
  • Space: O(1) - 只用常數變數
class Solution {
    fun minSwaps(s: String): Int {
        var unmatched = 0  // 未配對的 ] 數量
        var open = 0       // 可用的 [ 數量

        for (c in s) {
            if (c == '[') {
                open++
            } else {
                if (open > 0) {
                    open--
                } else {
                    unmatched++
                }
            }
        }

        return (unmatched + 1) / 2
    }
}

WARNING

為什麼 ⌈k/2⌉?消除配對後剩下 ]]]...[[[,每次交換最外層的一對 ][ 可以修復 2 個未配對。例如 ]][[ 只需 1 次交換變成 [[][]][[]]

🔑 Takeaways