title: 3Sum With Multiplicity
slug: 3sum-with-multiplicity
difficulty: medium
leetcode_id: 923
leetcode_url: https://leetcode.com/problems/3sum-with-multiplicity/
categories:
  - arrays
  - hash-tables
  - two-pointers
patterns:
  - two-pointers

description: |
  Given an integer array `arr`, and an integer `target`, return the number of tuples `i, j, k` such that `i < j < k` and `arr[i] + arr[j] + arr[k] == target`.

  As the answer can be very large, return it **modulo** `10^9 + 7`.

constraints: |
  - `3 <= arr.length <= 3000`
  - `0 <= arr[i] <= 100`
  - `0 <= target <= 300`

examples:
  - input: "arr = [1,1,2,2,3,3,4,4,5,5], target = 8"
    output: "20"
    explanation: "Enumerating by values: (1, 2, 5) occurs 8 times; (1, 3, 4) occurs 8 times; (2, 2, 4) occurs 2 times; (2, 3, 3) occurs 2 times."
  - input: "arr = [1,1,2,2,2,2], target = 5"
    output: "12"
    explanation: "arr[i] = 1, arr[j] = arr[k] = 2 occurs 12 times: We choose one 1 from [1,1] in 2 ways, and two 2s from [2,2,2,2] in 6 ways (C(4,2) = 6)."
  - input: "arr = [2,1,3], target = 6"
    output: "1"
    explanation: "(1, 2, 3) occurred one time in the array so we return 1."

explanation:
  intuition: |
    This problem is a twist on the classic 3Sum problem. Instead of finding unique triplets that sum to a target, we need to **count** all valid index combinations `(i, j, k)` where `i < j < k`.

    The key insight is that the array values are **bounded** (`0 <= arr[i] <= 100`). This small range means there are at most 101 distinct values, which opens up a counting-based approach.

    Think of it like this: instead of focusing on indices, we can focus on **values**. If we know the count of each value in the array, we can mathematically compute how many ways to pick indices that form a valid triplet.

    For three values `x`, `y`, `z` where `x + y + z == target`:
    - If all three are **distinct**: multiply their counts (`count[x] * count[y] * count[z]`)
    - If **two are equal** (e.g., `x == y`): use combinations formula (`C(count[x], 2) * count[z]`)
    - If **all three are equal** (`x == y == z`): use combinations formula (`C(count[x], 3)`)

    This transforms the problem from O(n³) index iteration to O(101³) value iteration — a massive improvement when `n` is large.

  approach: |
    We solve this using a **Counting Approach** that leverages the bounded value range:

    **Step 1: Count the frequency of each value**

    - Create a frequency array `count` of size 101 (since values range from 0 to 100)
    - Iterate through `arr` and increment `count[val]` for each value

    &nbsp;

    **Step 2: Iterate through all possible value triplets**

    - Use three nested loops for values `i`, `j`, `k` where `i <= j <= k`
    - This ensures we don't double-count triplets like (1, 2, 3) and (2, 1, 3)
    - Check if `i + j + k == target`

    &nbsp;

    **Step 3: Calculate combinations based on value equality**

    - **Case 1: All distinct** (`i < j < k`): Add `count[i] * count[j] * count[k]`
    - **Case 2: First two equal** (`i == j < k`): Add `C(count[i], 2) * count[k]` = `count[i] * (count[i] - 1) / 2 * count[k]`
    - **Case 3: Last two equal** (`i < j == k`): Add `count[i] * C(count[j], 2)` = `count[i] * count[j] * (count[j] - 1) / 2`
    - **Case 4: All equal** (`i == j == k`): Add `C(count[i], 3)` = `count[i] * (count[i] - 1) * (count[i] - 2) / 6`

    &nbsp;

    **Step 4: Apply modulo and return**

    - Keep result modulo `10^9 + 7` throughout to prevent overflow
    - Return the final count

  common_pitfalls:
    - title: The Brute Force Index Trap
      description: |
        A naive approach iterates through all index triplets `(i, j, k)` with three nested loops.

        With `n = 3000`, this means `3000³ = 27 billion` operations — far too slow. The problem constraints specifically allow values up to 100, hinting that we should count values rather than iterate indices.
      wrong_approach: "Triple nested loops over indices O(n³)"
      correct_approach: "Count frequencies and iterate over value combinations O(101³)"

    - title: Forgetting the Modulo
      description: |
        The answer can be astronomically large. With many duplicate values, the number of combinations grows exponentially.

        For example, an array of 3000 identical values that sum to the target would have `C(3000, 3) ≈ 4.5 billion` combinations. Always apply `% (10^9 + 7)` to intermediate results.
      wrong_approach: "Accumulating without modulo, causing integer overflow"
      correct_approach: "Apply modulo after each addition"

    - title: Double-Counting Triplets
      description: |
        When iterating values, you might accidentally count the triplet `(1, 2, 3)` multiple times if you don't enforce an ordering constraint.

        By ensuring `i <= j <= k` in your loops, each unique value triplet is considered exactly once.
      wrong_approach: "Unordered iteration over all value pairs"
      correct_approach: "Enforce i <= j <= k ordering"

    - title: Incorrect Combination Formulas
      description: |
        When two or more values are equal, you need the combinations formula, not simple multiplication.

        Picking 2 items from `n` identical items: `C(n, 2) = n * (n - 1) / 2`
        Picking 3 items from `n` identical items: `C(n, 3) = n * (n - 1) * (n - 2) / 6`

        Integer division must happen after the multiplication to avoid truncation errors.
      wrong_approach: "Using count² or count³ for repeated values"
      correct_approach: "Use proper combinations C(n, k) formulas"

  key_takeaways:
    - "**Counting over bounded domains**: When values are constrained to a small range, counting frequencies can reduce complexity dramatically"
    - "**Combinations for duplicates**: When selecting multiple items of the same type, use `C(n, k)` formulas rather than simple multiplication"
    - "**Modular arithmetic**: For large counts, apply modulo at each step to prevent overflow"
    - "**Order constraints prevent double-counting**: Enforcing `i <= j <= k` ensures each unique combination is counted exactly once"

  time_complexity: "O(n + V³) where V is the value range (101). We count frequencies in O(n), then iterate through at most 101³ ≈ 1 million value triplets."
  space_complexity: "O(V) where V is 101. We store a frequency count for each possible value in the range [0, 100]."

solutions:
  - approach_name: Counting with Combinatorics
    is_optimal: true
    code: |
      def three_sum_multi(arr: list[int], target: int) -> int:
          MOD = 10**9 + 7

          # Count frequency of each value (values range 0-100)
          count = [0] * 101
          for val in arr:
              count[val] += 1

          result = 0

          # Iterate through all value triplets where i <= j <= k
          for i in range(101):
              for j in range(i, 101):
                  k = target - i - j

                  # k must be in valid range and >= j (to avoid double counting)
                  if k < j or k > 100:
                      continue

                  if count[i] == 0 or count[j] == 0 or count[k] == 0:
                      continue

                  if i == j == k:
                      # All three values equal: C(count[i], 3)
                      result += count[i] * (count[i] - 1) * (count[i] - 2) // 6
                  elif i == j:
                      # First two equal: C(count[i], 2) * count[k]
                      result += count[i] * (count[i] - 1) // 2 * count[k]
                  elif j == k:
                      # Last two equal: count[i] * C(count[j], 2)
                      result += count[i] * count[j] * (count[j] - 1) // 2
                  else:
                      # All distinct: simple multiplication
                      result += count[i] * count[j] * count[k]

                  result %= MOD

          return result
    explanation: |
      **Time Complexity:** O(n + V²) where V = 101 — We count in O(n), then iterate i and j while computing k directly.

      **Space Complexity:** O(V) — Frequency array of size 101.

      By counting value frequencies first, we transform index-based enumeration into value-based enumeration. The combinations formulas handle duplicate values correctly, ensuring each valid index triplet is counted exactly once.

  - approach_name: Two Pointers with Sorting
    is_optimal: false
    code: |
      def three_sum_multi(arr: list[int], target: int) -> int:
          MOD = 10**9 + 7
          arr.sort()
          n = len(arr)
          result = 0

          for i in range(n - 2):
              # Two pointers for remaining sum
              left, right = i + 1, n - 1
              remaining = target - arr[i]

              while left < right:
                  current_sum = arr[left] + arr[right]

                  if current_sum < remaining:
                      left += 1
                  elif current_sum > remaining:
                      right -= 1
                  else:
                      # Found a valid triplet sum
                      if arr[left] == arr[right]:
                          # All values between left and right are equal
                          count = right - left + 1
                          result += count * (count - 1) // 2
                          break
                      else:
                          # Count duplicates on left and right
                          left_count = 1
                          while left + 1 < right and arr[left] == arr[left + 1]:
                              left += 1
                              left_count += 1

                          right_count = 1
                          while right - 1 > left and arr[right] == arr[right - 1]:
                              right -= 1
                              right_count += 1

                          result += left_count * right_count
                          left += 1
                          right -= 1

                  result %= MOD

          return result
    explanation: |
      **Time Complexity:** O(n² + n log n) — Sorting takes O(n log n), then we have O(n) iterations of the outer loop, each with O(n) two-pointer work.

      **Space Complexity:** O(1) or O(n) depending on sorting implementation.

      This approach sorts the array first, then uses the classic two-pointer technique for 3Sum. When duplicates are found, we count them and multiply. While this is less optimal than the counting approach for small value ranges, it works for any value range.
