Medium草稿★★★★★O(n) 時間 · O(n) 空間
StringHash TableSliding WindowBit Manipulation
Patterns🪟 滑動視窗💡 位元技巧#️⃣ 雜湊
尚未複習過

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

187Repeated DNA SequencesArrays & HashingMediumArrays & Hashing

給定 DNA 字串 s(只含 A, C, G, T),找出所有出現超過一次的長度為 10 的子字串。

Example:

Input: s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT” Output: [“AAAAACCCCC”,“CCCCCAAAAA”]

Intuition

TIP

核心思路:用 HashSet 記錄所有出現過的 10 字元子字串,若再次出現就加入結果集。

Approaches

1. HashSet of Strings — O(n * 10) / O(n * 10)
  • Idea: 用兩個 HashSet:seen 記錄出現過的子字串,repeated 記錄重複的子字串
  • Time: O(n * 10) - 每次取子字串 O(10)
  • Space: O(n * 10) - 儲存所有子字串
class Solution {
    fun findRepeatedDnaSequences(s: String): List<String> {
        if (s.length < 10) return emptyList()

        val seen = HashSet<String>()
        val repeated = HashSet<String>()

        for (i in 0..s.length - 10) {
            val sub = s.substring(i, i + 10)
            if (!seen.add(sub)) {
                repeated.add(sub)
            }
        }

        return repeated.toList()
    }
}
⭐ 2. Bitmask Rolling Hash — O(n) / O(n)
  • Idea: 每個字母用 2 bits 編碼(A=0, C=1, G=2, T=3),10 個字母用 20 bits 表示,用整數代替字串作為 HashSet 的 key
  • Time: O(n) - 每次滑動 O(1) 更新 hash
  • Space: O(n) - HashSet 儲存整數而非字串,更省空間
class Solution {
    fun findRepeatedDnaSequences(s: String): List<String> {
        if (s.length < 10) return emptyList()

        val charToVal = mapOf('A' to 0, 'C' to 1, 'G' to 2, 'T' to 3)
        val mask = (1 shl 20) - 1  // 20 bits mask

        var hash = 0
        // 初始化前 9 個字元的 hash
        for (i in 0 until 9) {
            hash = (hash shl 2) or charToVal[s[i]]!!
        }

        val seen = HashSet<Int>()
        val repeated = HashSet<Int>()
        val result = mutableListOf<String>()

        for (i in 9 until s.length) {
            hash = ((hash shl 2) or charToVal[s[i]]!!) and mask
            if (!seen.add(hash)) {
                if (repeated.add(hash)) {
                    result.add(s.substring(i - 9, i + 1))
                }
            }
        }

        return result
    }
}

WARNING

Bitmask 編碼的好處:整數比較比字串比較快,且記憶體使用大幅降低。4 個字母只需要 2 bits,10 個字母的子字串用 20 bits 就能唯一表示,完全可以放在一個 Int 中。

🔑 Takeaways