Facebook Pixel

3583. Count Special Triplets

MediumArrayHash TableCounting
Leetcode Link

Problem Description

You are given an integer array nums.

A special triplet is defined as a triplet of indices (i, j, k) such that:

  • 0 <= i < j < k < n, where n = nums.length
  • nums[i] == nums[j] * 2
  • nums[k] == nums[j] * 2

Return the total number of special triplets in the array.

Since the answer may be large, return it modulo 10^9 + 7.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The task is to count triplets (i, j, k) where nums[i] and nums[k] are both exactly double nums[j], with the indices in order (i < j < k). Instead of checking all possible triplets, which would be slow for large arrays, notice that for each possible "middle" element nums[j], we only care about how many times nums[j] * 2 appears both before and after index j.

If we can quickly count those occurrences, then for each j, the total number of triplets with j as the middle is just the product of those two counts. This can be done efficiently by keeping track of counts to the left and right of j as we iterate through the array. Using hash tables (or Counters) to store these counts lets us compute the answer in one pass, updating our counts as we go.

Solution Approach

The solution uses an efficient way to count the valid triplets by focusing on each possible "middle" index j and using hash tables to keep track of occurrences:

  1. Use two hash tables, left and right, where:
    • left[x] tracks how many times number x has appeared before the current index.
    • right[x] tracks how many times number x will appear after the current index.
  2. Initially, count all occurrences of each number in nums and store them in right. The left hash table starts empty.
  3. Iterate over the array from left to right using index j as the "middle" of the triplet:
    • For each nums[j], decrement its count in right since it's now being considered.
    • To form a valid triplet (i, j, k), need:
      • i < j where nums[i] == nums[j] * 2 (count in left[nums[j] * 2])
      • k > j where nums[k] == nums[j] * 2 (count in right[nums[j] * 2])
    • Multiply the counts for left[nums[j] * 2] and right[nums[j] * 2] to get all ways to choose i and k for given j, and add to the answer.
    • After processing, increment the count of nums[j] in left.
  4. Since the result can be large, keep the answer modulo 10^9 + 7.

Here is the code that implements this logic:

from collections import Counter

class Solution:
    def specialTriplets(self, nums: List[int]) -> int:
        left = Counter()
        right = Counter(nums)
        ans = 0
        mod = 10**9 + 7
        for x in nums:
            right[x] -= 1
            ans = (ans + left[x * 2] * right[x * 2] % mod) % mod
            left[x] += 1
        return ans
  • The algorithm runs in linear time, iterating through nums once and using constant work per position with hash table lookups and updates. This makes it very efficient for large arrays.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's illustrate the solution with a small array: nums = [4, 2, 4, 8, 2]

We want to count the number of special triplets (i, j, k) such that:

  • 0 <= i < j < k < 5
  • nums[i] == nums[j] * 2
  • nums[k] == nums[j] * 2

Step-by-step

1. Initialization

  • left = Counter()          (Initially empty)
  • right = Counter({4: 2, 2: 2, 8: 1})  (Counts of all elements)
  • ans = 0

2. Iterate through each j (the middle index):


j = 0, nums[0] = 4

  • Decrement right[4]right[4] = 1
  • Count of nums[j]*2 in left = left[8] = 0
  • Count of nums[j]*2 in right = right[8] = 1
  • Triplets for this j: 0 * 1 = 0 (add to ans)
  • Increment left[4]left[4] = 1

j = 1, nums[1] = 2

  • Decrement right[2]right[2] = 1
  • nums[j]*2 = 4
  • left[4] = 1, right[4] = 1
  • Triplets: 1 * 1 = 1 (add to ans, now ans = 1)
  • Increment left[2]left[2] = 1

j = 2, nums[2] = 4

  • Decrement right[4]right[4] = 0
  • nums[j]*2 = 8
  • left[8] = 0, right[8] = 1
  • Triplets: 0 * 1 = 0
  • Increment left[4]left[4] = 2

j = 3, nums[3] = 8

  • Decrement right[8]right[8] = 0
  • nums[j]*2 = 16
  • left[16] = 0, right[16] = 0
  • Triplets: 0 * 0 = 0
  • Increment left[8]left[8] = 1

j = 4, nums[4] = 2

  • Decrement right[2]right[2] = 0
  • nums[j]*2 = 4
  • left[4]=2, right[4]=0
  • Triplets: 2 * 0 = 0
  • Increment left[2]left[2] = 2

Final count

The total number of special triplets in [4, 2, 4, 8, 2] is 1.


This process efficiently counts triplets by only tracking how many valid i (before j) and k (after j) can pair up with each choice of j, avoiding slow triple-nested loops.

Solution Implementation

1from collections import Counter
2from typing import List
3
4class Solution:
5    def specialTriplets(self, nums: List[int]) -> int:
6        # Counter to keep track of numbers to the left of current element
7        left = Counter()
8        # Counter to keep track of numbers to the right of current element
9        right = Counter(nums)
10        ans = 0
11        mod = 10**9 + 7  # Large prime for modulo
12
13        # Iterate through each number in nums
14        for x in nums:
15            # Decrement current number from right as we're processing it now
16            right[x] -= 1
17
18            # Count combinations where both in left and right
19            # There is a number equal to 2 * x
20            count_left = left[2 * x]
21            count_right = right[2 * x]
22            ans = (ans + count_left * count_right % mod) % mod
23
24            # Add current number to the left counter
25            left[x] += 1
26
27        return ans
28
1class Solution {
2    public int specialTriplets(int[] nums) {
3        // Frequency count of elements that appear to the left of the current index.
4        Map<Integer, Integer> leftCounts = new HashMap<>();
5        // Frequency count of elements that appear to the right of the current index.
6        Map<Integer, Integer> rightCounts = new HashMap<>();
7
8        // Initialize rightCounts with the frequency of each element in nums.
9        for (int num : nums) {
10            rightCounts.merge(num, 1, Integer::sum);
11        }
12
13        long countTriplets = 0;
14        final int MOD = 1_000_000_007;
15
16        // Iterate through each element, treating it as the "middle" of the triplet.
17        for (int num : nums) {
18            // Move num from rightCounts to leftCounts
19            rightCounts.merge(num, -1, Integer::sum);
20
21            // Number we're looking for is 2 * num
22            int target = num * 2;
23
24            // Count pairs where left and right both have 'target'
25            int leftFrequency = leftCounts.getOrDefault(target, 0);
26            int rightFrequency = rightCounts.getOrDefault(target, 0);
27
28            // Update the answer with the combinations of matching targets on both sides
29            countTriplets = (countTriplets + 1L * leftFrequency * rightFrequency % MOD) % MOD;
30
31            // Update leftCounts for next iteration
32            leftCounts.merge(num, 1, Integer::sum);
33        }
34
35        return (int) countTriplets;
36    }
37}
38
1class Solution {
2public:
3    int specialTriplets(vector<int>& nums) {
4        // Maps to count occurrences of values to the left and right of current position
5        unordered_map<int, int> left, right;
6
7        // Initialize right map with counts of all numbers in nums
8        for (int num : nums) {
9            right[num]++;
10        }
11
12        long long ans = 0;
13        const int mod = 1e9 + 7;
14
15        // Iterate through nums, treating each number as the 'middle' of the triplet
16        for (int num : nums) {
17            // Move current num from 'right' to 'left' (i.e., shift window)
18            right[num]--;
19
20            // For current num, count the pairs where both left and right have (num * 2)
21            // This counts (i, j, k) such that nums[i] == nums[k] == 2 * nums[j] and i < j < k
22            ans = (ans + 1LL * left[num * 2] * right[num * 2] % mod) % mod;
23
24            // Increment count of current num in 'left'
25            left[num]++;
26        }
27
28        return static_cast<int>(ans);
29    }
30};
31
1/**
2 * Counts the number of special triplets in the array.
3 * A triplet (i, j, k) is special if nums[j] == 2 * nums[i] and nums[k] == 2 * nums[j].
4 * @param nums Array of integers
5 * @returns The number of special triplets modulo 1e9 + 7
6 */
7function specialTriplets(nums: number[]): number {
8    // Maps to store the frequency of numbers on the left and right of the current index
9    const leftCount = new Map<number, number>();
10    const rightCount = new Map<number, number>();
11
12    // Populate rightCount map with frequency of each number
13    for (const num of nums) {
14        rightCount.set(num, (rightCount.get(num) || 0) + 1);
15    }
16
17    let tripletCount = 0;
18    const MOD = 1e9 + 7;
19
20    // Iterate through each number in nums as the middle element of the triplet
21    for (const num of nums) {
22        // Decrement the current number from the right since it's now being considered
23        rightCount.set(num, (rightCount.get(num) || 0) - 1);
24
25        // Count of candidates for left side (nums[i] == num / 2)
26        const leftCandidates = leftCount.get(num * 2) || 0;
27
28        // Count of candidates for right side (nums[k] == num * 2)
29        const rightCandidates = rightCount.get(num * 2) || 0;
30
31        // Add product to the result, modulo MOD
32        tripletCount = (tripletCount + ((leftCandidates * rightCandidates) % MOD)) % MOD;
33
34        // Increment the left side map for future iterations
35        leftCount.set(num, (leftCount.get(num) || 0) + 1);
36    }
37
38    return tripletCount;
39}
40

Time and Space Complexity

The time complexity is O(n), and the space complexity is O(n), where n is the length of the array nums.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More