🔍 配列ペア数算出アルゴリズムの詳細解析

📋 問題定義

目標: 配列内で 1 ≤ j < i ≤ N かつ Aj = Ai を満たすペア(i, j)の数を求める

制約: N ≤ 100,000、Ai ≤ 10^9、実行時間 ≤ 2秒、メモリ ≤ 1024MB

🎯 アルゴリズム概要

入力読み込み & 解析
各値の出現回数をカウント
組み合わせ数を計算
結果出力

📊 ステップ1: 入力データの処理

入力例:

6
30
10
30
20
10
30

配列表現

0
30
1
10
2
30
3
20
4
10
5
30

🗂️ ステップ2: 出現回数のカウント

処理: Map<値, 出現回数>を構築

for (const num of arr) {
  const currentCount = countMap.get(num) ?? 0;
  countMap.set(num, currentCount + 1);
}

処理後のMap状態

値: 30
出現: 3回
値: 10
出現: 2回
値: 20
出現: 1回

🔢 ステップ3: 組み合わせ数の計算

公式: k個の同じ値から2個を選ぶ組み合わせ = C(k,2) = k × (k-1) / 2

値 30 (3回出現)

C(3,2) = 3 × 2 / 2 = 3
具体的なペア:
• (2,0): A₂=A₀=30
• (5,0): A₅=A₀=30
• (5,2): A₅=A₂=30

値 10 (2回出現)

C(2,2) = 2 × 1 / 2 = 1
具体的なペア:
• (4,1): A₄=A₁=10

値 20 (1回出現)

C(1,2) = 0
具体的なペア:
なし(1個だけなのでペアを作れない)
総ペア数 = 3 + 1 + 0 = 4

⚡ 計算量解析

処理フェーズ 時間計算量 空間計算量 詳細
入力読み込み O(N) O(N) 配列全体を一度読み込み
出現回数カウント O(N) O(K) Kは異なる値の数(最悪でO(N))
組み合わせ計算 O(K) O(1) Map内の各エントリを1回処理
総合計算量 O(N) O(N) 線形時間で効率的

🚀 最適化のポイント

1. メモリ効率

2. 処理時間

3. 精度と安全性

🎯 実装の核心コード

function countPairs(arr: number[]): number {
  // Step 1: 出現回数をカウント
  const countMap = new Map<number, number>();
  for (const num of arr) {
    const currentCount = countMap.get(num) ?? 0;
    countMap.set(num, currentCount + 1);
  }

  // Step 2: 組み合わせ数を計算
  let totalPairs: number = 0;
  for (const count of countMap.values()) {
    if (count >= 2) {
      totalPairs += Math.floor((count * (count - 1)) / 2);
    }
  }
  return totalPairs;
}

✅ 制約チェック

制約項目 制限値 実装での対応 安全マージン
配列サイズ N ≤ 100,000 O(N)の線形処理 ✅ 十分に高速
実行時間 ≤ 2秒 〜0.01秒 (推定) ✅ 200倍のマージン
メモリ使用量 ≤ 1024MB 〜8MB (推定) ✅ 128倍のマージン
値の範囲 Ai ≤ 10^9 Number型で安全に処理 ✅ 完全対応