Description

給定字串 s、字串 p 和陣列 removable(含 s 的索引)。依序移除 removable 中前 k 個索引對應的字元後,p 仍必須是 s 的子序列。求最大的 k

Example:

Input: s = “abcacb”, p = “ab”, removable = [3,1,0] Output: 2(移除索引 3, 1 後 s 變成 “a_c_cb”,p=“ab” 仍是子序列)

Intuition#

k 越大越不可能滿足,具有單調性。在 [0, removable.size] 上二分搜尋最大的 k。

  • 移除越多字元,p 越不容易是 s 的子序列 – 單調性
  • 對於給定的 k,標記前 k 個 removable 索引為「已刪除」,然後檢查 p 是否仍是 s 的子序列

Approaches#

1: Binary Search on Answer#

  • 概念: 在 [0, removable.size] 上二分搜尋最大的 k,用子序列檢查驗證
  • 時間複雜度: O((n + m) * log(removable.size)),n = |s|, m = removable.size
  • 空間複雜度: O(n)(標記陣列)
class Solution {
    fun maximumRemovals(s: String, p: String, removable: IntArray): Int {
        var left = 0
        var right = removable.size
        while (left < right) {
            val mid = left + (right - left + 1) / 2
            if (isSubsequence(s, p, removable, mid)) {
                left = mid
            } else {
                right = mid - 1
            }
        }
        return left
    }

    private fun isSubsequence(s: String, p: String, removable: IntArray, k: Int): Boolean {
        val removed = BooleanArray(s.length)
        for (i in 0 until k) {
            removed[removable[i]] = true
        }
        var j = 0
        for (i in s.indices) {
            if (!removed[i] && j < p.length && s[i] == p[j]) {
                j++
            }
        }
        return j == p.length
    }
}

這是找最大可行值,使用 left = mid 配合向上取整的 mid。子序列判斷使用雙指標,跳過已標記刪除的字元。

🔑 Takeaways#

  • Pattern: Binary Search on Answer(找最大可行值)
  • 關鍵技巧: 識別單調性是關鍵 – 移除越多字元越不可能滿足條件。驗證函式用 boolean 陣列標記刪除位置,再做子序列判斷