Description

給定字串陣列 ideas,每個字串代表一個公司名。選擇兩個名字 ideaAideaB,交換它們的首字母得到 ideaA'ideaB'。若兩個新名字都不在原始 ideas 中,則 (ideaA', ideaB') 是一個有效的公司名組合。回傳不同的有效名稱數量。

Example:

Input: ideas = [“coffee”,“donuts”,“time”,“toffee”] Output: 6 (如 (“doffee”,“conuts”), (“toffee”,“cime”) 等)

Intuition#

核心思路:按首字母分組,對任意兩組(首字母 a 和 b),計算「交換後互不衝突」的配對數。若 suffix 同時出現在 a 組和 b 組,交換後兩個新名字都會衝突,所以要排除共同 suffix。

  • 將所有 idea 按首字母分成 26 組,每組存 suffix Set
  • 對任意兩組 (i, j),共同 suffix 的數量為 mutual = |set_i ∩ set_j|
  • 可配對的 suffix 數量:(|set_i| - mutual) * (|set_j| - mutual)
  • 乘以 2(因為 (A, B) 和 (B, A) 是不同的)

Approaches#

1: Brute Force (TLE)#

  • 概念: 枚舉所有 idea 對,交換首字母檢查是否有效
  • 時間複雜度: O(n^2 * m) - n 為 idea 數,m 為平均長度
  • 空間複雜度: O(n * m) - HashSet 儲存所有 idea
class Solution {
    fun distinctNames(ideas: Array<String>): Long {
        val ideaSet = ideas.toHashSet()
        var count = 0L

        for (i in ideas.indices) {
            for (j in i + 1 until ideas.size) {
                val newA = ideas[j][0] + ideas[i].substring(1)
                val newB = ideas[i][0] + ideas[j].substring(1)
                if (newA !in ideaSet && newB !in ideaSet) {
                    count += 2
                }
            }
        }
        return count
    }
}

⭐2: Group by Initial + Mutual Exclusion#

  • 概念: 按首字母分 26 組,對每對組計算共同 suffix 數量,用排除法計算有效配對
  • 時間複雜度: O(n * m + 26^2 * n) - 分組 O(n*m),計算交集最壞 O(26^2 * max_group_size)
  • 空間複雜度: O(n * m) - 儲存所有 suffix Set
class Solution {
    fun distinctNames(ideas: Array<String>): Long {
        // 按首字母分組,存 suffix
        val groups = Array(26) { HashSet<String>() }
        for (idea in ideas) {
            groups[idea[0] - 'a'].add(idea.substring(1))
        }

        var count = 0L

        for (i in 0 until 26) {
            for (j in i + 1 until 26) {
                // 計算共同 suffix 數量
                var mutual = 0
                for (suffix in groups[i]) {
                    if (suffix in groups[j]) mutual++
                }

                val uniqueI = groups[i].size - mutual
                val uniqueJ = groups[j].size - mutual

                count += 2L * uniqueI * uniqueJ
            }
        }

        return count
    }
}

為什麼只需要考慮共同 suffix?假設 suffix “offee” 同時出現在 ‘c’ 組(“coffee”)和 ’t’ 組(“toffee”)。交換首字母後 “toffee” 和 “coffee” 都已存在於原始 ideas,所以這對 suffix 不能配對。只有兩組中「互不共享」的 suffix 才能安全交換。

🔑 Takeaways#

  • Pattern: 大量配對計數 -> 分組 + 排除共同元素
  • 關鍵技巧: 按首字母分組將 O(n^2) 問題降維到 O(26^2 _ group_size);「互斥計數」公式 (|A| - |A∩B|) _ (|B| - |A∩B|) 是分組配對的通用模式