Easy草稿★★★★★O(n) 時間 · O(1) 空間
Two PointersString
Patterns↔️ 雙指針
尚未複習過

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

392Is SubsequenceArrays & HashingEasyArrays & Hashing

給定兩個字串 st,判斷 s 是否是 t 的子序列。子序列是從原字串中刪除若干(或零個)字元後,不改變剩餘字元相對順序所形成的新字串。

Example:

Input: s = “abc”, t = “ahbgdc” Output: true

Intuition

TIP

核心思路:雙指標,一個掃 s、一個掃 t,在 t 中依序找到 s 的每個字元即可。

Approaches

1. Brute Force (recursive) — O(n) / O(n)
  • Idea: 遞迴檢查,每次嘗試匹配 s[i]t[j]
  • Time: O(n) - n 為 t 的長度
  • Space: O(n) - 遞迴呼叫堆疊
class Solution {
    fun isSubsequence(s: String, t: String): Boolean {
        return helper(s, t, 0, 0)
    }

    private fun helper(s: String, t: String, i: Int, j: Int): Boolean {
        if (i == s.length) return true
        if (j == t.length) return false
        return if (s[i] == t[j]) {
            helper(s, t, i + 1, j + 1)
        } else {
            helper(s, t, i, j + 1)
        }
    }
}
⭐ 2. Two Pointers — O(n) / O(1)
  • Idea: 用兩個指標分別指向 st,匹配則同時前進,不匹配則只移動 t 的指標
  • Time: O(n) - n 為 t 的長度
  • Space: O(1) - 只用兩個指標
class Solution {
    fun isSubsequence(s: String, t: String): Boolean {
        var i = 0
        var j = 0
        while (i < s.length && j < t.length) {
            if (s[i] == t[j]) {
                i++
            }
            j++
        }
        return i == s.length
    }
}
Note: Follow-up Preprocessing for Many Queries

如果有大量 s 需要檢查是否為同一個 t 的子序列,可以預處理 t,建立每個字元下一次出現位置的查找表,用二分搜尋加速。

class Solution {
    fun isSubsequence(s: String, t: String): Boolean {
        // 預處理:記錄 t 中每個字元出現的所有索引
        val indexMap = HashMap<Char, MutableList<Int>>()
        for ((i, c) in t.withIndex()) {
            indexMap.getOrPut(c) { mutableListOf() }.add(i)
        }

        var prev = -1
        for (c in s) {
            val indices = indexMap[c] ?: return false
            // 二分搜尋找到第一個 > prev 的索引
            val pos = indices.binarySearch(prev + 1).let {
                if (it >= 0) it else -(it + 1)
            }
            if (pos >= indices.size) return false
            prev = indices[pos]
        }
        return true
    }
}

🔑 Takeaways