Medium草稿★★★★★O(n) 時間 · O(n) 空間
StackString
Patterns🥞 堆疊
尚未複習過

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

1209Remove All Adjacent Duplicates In String IIStackMediumStack

給定字串 s 和整數 k,反覆從字串中移除由 k 個相同字元組成的相鄰重複子串,直到無法再移除為止。回傳最終結果。

Example:

Input: s = “deeedbbcccbdaa”, k = 3 Output: “aa”

Intuition

TIP

核心思路:用堆疊記錄 (字元, 連續次數),當次數達到 k 時彈出,自動處理消除後的連鎖反應。

Approaches

1. Brute Force — O(n^2 / k) / O(n)
  • Idea: 反覆掃描字串,找到 k 個連續相同字元就刪除,直到沒有可刪除的為止
  • Time: O(n^2 / k) - 最壞情況
  • Space: O(n)
class Solution {
    fun removeDuplicates(s: String, k: Int): String {
        var str = StringBuilder(s)
        var changed = true

        while (changed) {
            changed = false
            var count = 1
            for (i in 1 until str.length) {
                if (str[i] == str[i - 1]) {
                    count++
                    if (count == k) {
                        str.delete(i - k + 1, i + 1)
                        changed = true
                        break
                    }
                } else {
                    count = 1
                }
            }
        }

        return str.toString()
    }
}
⭐ 2. Stack Counting — O(n) / O(n)
  • Idea: 堆疊存 Pair(字元, 計數),新字元與棧頂相同就加一,達到 k 就移除,否則 push 新配對
  • Time: O(n) - 每個字元最多入棧出棧各一次
  • Space: O(n)
class Solution {
    fun removeDuplicates(s: String, k: Int): String {
        // stack 存 (字元, 連續出現次數)
        val stack = ArrayDeque<Pair<Char, Int>>()

        for (c in s) {
            if (stack.isNotEmpty() && stack.last().first == c) {
                val newCount = stack.last().second + 1
                if (newCount == k) {
                    stack.removeLast()
                } else {
                    stack.removeLast()
                    stack.addLast(c to newCount)
                }
            } else {
                stack.addLast(c to 1)
            }
        }

        val sb = StringBuilder()
        for ((ch, count) in stack) {
            repeat(count) { sb.append(ch) }
        }
        return sb.toString()
    }
}

🔑 Takeaways