Facebook Pixel

2672. Number of Adjacent Elements With the Same Color

Problem Description

You start with an array of length n where all elements are initially 0 (representing uncolored positions). You're given a series of queries, where each query contains an index and a color value.

For each query [index, color], you need to:

  1. Color the element at the given index with the specified color (set colors[index] = color)
  2. Count how many adjacent pairs in the entire array have the same color after this coloring

An adjacent pair means two consecutive elements in the array (elements at positions i and i+1). Two adjacent elements form a matching pair if they have the same color value, and that color is not 0 (uncolored elements don't count as matching).

The solution tracks the number of matching adjacent pairs efficiently by:

  • Before changing a position's color, it checks if that position currently forms matching pairs with its neighbors (left and right), and decreases the count accordingly
  • After assigning the new color, it checks if the new color creates matching pairs with its neighbors and increases the count
  • The variable x maintains the running count of matching adjacent pairs
  • For each query, the current count is stored in the answer array

For example, if you have an array [1, 1, 2, 0, 0], there is exactly 1 matching adjacent pair (the two 1's at positions 0 and 1). If you then color position 2 with color 1, the array becomes [1, 1, 1, 0, 0] and you now have 2 matching adjacent pairs.

The function returns an array where each element represents the total number of matching adjacent pairs after processing each query in order.

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

Intuition

The key insight is that when we color a position, we only need to consider how this change affects the adjacent pairs involving that specific position - not the entire array. Each position can form at most two adjacent pairs: one with its left neighbor and one with its right neighbor.

When we update colors[i] to a new color, we're potentially changing two adjacent pairs:

  • The pair (colors[i-1], colors[i])
  • The pair (colors[i], colors[i+1])

Instead of recounting all adjacent pairs from scratch after each query (which would be O(n) per query), we can maintain a running count and just update it based on the local changes.

The approach is to:

  1. Remove the old contributions: Before changing colors[i], check if the current value forms any matching pairs with its neighbors. If it does, we need to subtract these from our count since they won't exist after the change.
  2. Add the new contributions: After setting the new color, check if this new color creates any matching pairs with the neighbors and add these to our count.

This incremental update strategy is efficient because:

  • We only examine at most 2 positions (the left and right neighbors) per query
  • We maintain the global count x throughout all queries, updating it incrementally
  • Each query takes O(1) time instead of O(n)

The careful handling of boundary conditions (when i = 0 or i = n-1) and checking that colors are non-zero (since 0 represents uncolored) ensures we count only valid matching pairs.

Solution Approach

The solution uses a simulation approach with incremental updates to efficiently track the number of matching adjacent pairs.

Data Structures:

  • nums: An array of size n initialized with zeros to represent the current state of colors
  • ans: Result array to store the count of matching pairs after each query
  • x: A running counter that maintains the current number of matching adjacent pairs

Algorithm Implementation:

For each query (i, c) where we need to color position i with color c:

  1. Remove contributions from the old color at position i:

    • Check left neighbor: If i > 0 and nums[i] != 0 and nums[i-1] == nums[i], decrement x by 1
    • Check right neighbor: If i < n-1 and nums[i] != 0 and nums[i+1] == nums[i], decrement x by 1

    These checks ensure we only decrement when there's an existing matching pair that will be broken.

  2. Add contributions from the new color c:

    • Check left neighbor: If i > 0 and nums[i-1] == c, increment x by 1
    • Check right neighbor: If i < n-1 and nums[i+1] == c, increment x by 1

    These checks determine if the new color will create matching pairs with neighbors.

  3. Update and record:

    • Store the current value of x in ans[k] for the k-th query
    • Update nums[i] = c to reflect the new color

The order of operations is crucial: we must update the count before actually changing the color in the array, otherwise we'd lose information about the previous state.

Time Complexity: O(m) where m is the number of queries, since each query processes in O(1) time.

Space Complexity: O(n) for the nums array and O(m) for the answer array.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with n = 5 and queries [[1,2], [2,2], [0,1], [3,1]].

Initial State:

  • Array: [0, 0, 0, 0, 0]
  • Matching pairs count x = 0

Query 1: [1, 2] - Color position 1 with color 2

  • Check position 1's current contributions:
    • Left neighbor (position 0): nums[1] = 0, so no existing pair to remove
    • Right neighbor (position 2): nums[1] = 0, so no existing pair to remove
  • Apply new color 2 at position 1:
    • Left neighbor check: nums[0] = 0 ≠ 2, no new pair
    • Right neighbor check: nums[2] = 0 ≠ 2, no new pair
  • Update: nums[1] = 2, x = 0
  • Array: [0, 2, 0, 0, 0], Answer: [0]

Query 2: [2, 2] - Color position 2 with color 2

  • Check position 2's current contributions:
    • Left neighbor (position 1): nums[2] = 0, so no existing pair to remove
    • Right neighbor (position 3): nums[2] = 0, so no existing pair to remove
  • Apply new color 2 at position 2:
    • Left neighbor check: nums[1] = 2 = 2, creates a new pair! x++
    • Right neighbor check: nums[3] = 0 ≠ 2, no new pair
  • Update: nums[2] = 2, x = 1
  • Array: [0, 2, 2, 0, 0], Answer: [0, 1]

Query 3: [0, 1] - Color position 0 with color 1

  • Check position 0's current contributions:
    • No left neighbor (boundary)
    • Right neighbor (position 1): nums[0] = 0, so no existing pair to remove
  • Apply new color 1 at position 0:
    • No left neighbor to check
    • Right neighbor check: nums[1] = 2 ≠ 1, no new pair
  • Update: nums[0] = 1, x = 1
  • Array: [1, 2, 2, 0, 0], Answer: [0, 1, 1]

Query 4: [3, 1] - Color position 3 with color 1

  • Check position 3's current contributions:
    • Left neighbor (position 2): nums[3] = 0, so no existing pair to remove
    • Right neighbor (position 4): nums[3] = 0, so no existing pair to remove
  • Apply new color 1 at position 3:
    • Left neighbor check: nums[2] = 2 ≠ 1, no new pair
    • Right neighbor check: nums[4] = 0 ≠ 1, no new pair
  • Update: nums[3] = 1, x = 1
  • Array: [1, 2, 2, 1, 0], Answer: [0, 1, 1, 1]

Final Result: [0, 1, 1, 1]

The array [1, 2, 2, 1, 0] has exactly one matching adjacent pair: positions 1 and 2 both have color 2.

Solution Implementation

1class Solution:
2    def colorTheArray(self, n: int, queries: List[List[int]]) -> List[int]:
3        # Initialize array with all elements as 0 (uncolored)
4        colors = [0] * n
5      
6        # Result array to store count after each query
7        result = [0] * len(queries)
8      
9        # Running count of adjacent elements with same non-zero color
10        adjacent_same_color_count = 0
11      
12        # Process each query
13        for query_index, (position, new_color) in enumerate(queries):
14            # Remove contribution of current element before updating
15            # Check if current element forms a pair with left neighbor
16            if position > 0 and colors[position] != 0 and colors[position - 1] == colors[position]:
17                adjacent_same_color_count -= 1
18          
19            # Check if current element forms a pair with right neighbor
20            if position < n - 1 and colors[position] != 0 and colors[position + 1] == colors[position]:
21                adjacent_same_color_count -= 1
22          
23            # Add contribution of new color
24            # Check if new color will form a pair with left neighbor
25            if position > 0 and colors[position - 1] == new_color and new_color != 0:
26                adjacent_same_color_count += 1
27          
28            # Check if new color will form a pair with right neighbor
29            if position < n - 1 and colors[position + 1] == new_color and new_color != 0:
30                adjacent_same_color_count += 1
31          
32            # Store current count in result
33            result[query_index] = adjacent_same_color_count
34          
35            # Update the color at the specified position
36            colors[position] = new_color
37      
38        return result
39
1class Solution {
2    public int[] colorTheArray(int n, int[][] queries) {
3        int queryCount = queries.length;
4        int[] colors = new int[n]; // Array to store colors, initially all 0 (uncolored)
5        int[] result = new int[queryCount]; // Result array to store count after each query
6        int adjacentPairs = 0; // Running count of adjacent elements with same color
7      
8        for (int queryIndex = 0; queryIndex < queryCount; queryIndex++) {
9            int position = queries[queryIndex][0];
10            int newColor = queries[queryIndex][1];
11          
12            // Before changing the color, remove existing adjacent pairs involving this position
13          
14            // Check if current position and left neighbor form a pair (both colored and same color)
15            if (position > 0 && colors[position] > 0 && colors[position - 1] == colors[position]) {
16                adjacentPairs--;
17            }
18          
19            // Check if current position and right neighbor form a pair (both colored and same color)
20            if (position < n - 1 && colors[position] > 0 && colors[position + 1] == colors[position]) {
21                adjacentPairs--;
22            }
23          
24            // After changing the color, add new adjacent pairs involving this position
25          
26            // Check if new color will form a pair with left neighbor
27            if (position > 0 && colors[position - 1] == newColor) {
28                adjacentPairs++;
29            }
30          
31            // Check if new color will form a pair with right neighbor
32            if (position < n - 1 && colors[position + 1] == newColor) {
33                adjacentPairs++;
34            }
35          
36            // Store the current count of adjacent pairs
37            result[queryIndex] = adjacentPairs;
38          
39            // Apply the new color to the position
40            colors[position] = newColor;
41        }
42      
43        return result;
44    }
45}
46
1class Solution {
2public:
3    vector<int> colorTheArray(int n, vector<vector<int>>& queries) {
4        // Initialize array with all elements as 0 (uncolored)
5        vector<int> colors(n, 0);
6      
7        // Store results for each query
8        vector<int> result;
9      
10        // Track the current count of adjacent pairs with same color
11        int adjacentPairCount = 0;
12      
13        // Process each query
14        for (const auto& query : queries) {
15            int index = query[0];
16            int newColor = query[1];
17          
18            // Before coloring, check if current position creates any adjacent pairs
19            // We need to subtract these pairs from the count
20          
21            // Check left neighbor: if current element and left neighbor both have color and match
22            if (index > 0 && colors[index] > 0 && colors[index - 1] == colors[index]) {
23                adjacentPairCount--;
24            }
25          
26            // Check right neighbor: if current element and right neighbor both have color and match
27            if (index < n - 1 && colors[index] > 0 && colors[index + 1] == colors[index]) {
28                adjacentPairCount--;
29            }
30          
31            // After coloring with new color, check if new adjacent pairs are formed
32            // We need to add these new pairs to the count
33          
34            // Check if new color matches left neighbor
35            if (index > 0 && colors[index - 1] == newColor) {
36                adjacentPairCount++;
37            }
38          
39            // Check if new color matches right neighbor
40            if (index < n - 1 && colors[index + 1] == newColor) {
41                adjacentPairCount++;
42            }
43          
44            // Store the current count of adjacent pairs after this query
45            result.push_back(adjacentPairCount);
46          
47            // Apply the new color to the current position
48            colors[index] = newColor;
49        }
50      
51        return result;
52    }
53};
54
1/**
2 * Colors an array based on queries and tracks adjacent elements with same colors
3 * @param n - The size of the array to color
4 * @param queries - Array of [index, color] pairs representing coloring operations
5 * @returns Array containing count of adjacent same-colored pairs after each query
6 */
7function colorTheArray(n: number, queries: number[][]): number[] {
8    // Initialize array with all elements as 0 (uncolored)
9    const colorArray: number[] = new Array(n).fill(0);
10  
11    // Store results for each query
12    const results: number[] = [];
13  
14    // Track count of adjacent elements with same non-zero colors
15    let adjacentPairsCount: number = 0;
16  
17    // Process each query
18    for (const [index, newColor] of queries) {
19        // Before coloring, check if current position creates any adjacent pairs
20      
21        // Check left neighbor: if current element and left neighbor both have same non-zero color
22        if (index > 0 && colorArray[index] > 0 && colorArray[index - 1] === colorArray[index]) {
23            // Removing this pair since we're about to change the color
24            adjacentPairsCount--;
25        }
26      
27        // Check right neighbor: if current element and right neighbor both have same non-zero color
28        if (index < n - 1 && colorArray[index] > 0 && colorArray[index + 1] === colorArray[index]) {
29            // Removing this pair since we're about to change the color
30            adjacentPairsCount--;
31        }
32      
33        // After coloring, check if new color creates any adjacent pairs
34      
35        // Check if new color matches left neighbor
36        if (index > 0 && colorArray[index - 1] === newColor) {
37            // Adding a new adjacent pair
38            adjacentPairsCount++;
39        }
40      
41        // Check if new color matches right neighbor
42        if (index < n - 1 && colorArray[index + 1] === newColor) {
43            // Adding a new adjacent pair
44            adjacentPairsCount++;
45        }
46      
47        // Record current count of adjacent pairs
48        results.push(adjacentPairsCount);
49      
50        // Apply the new color to the array
51        colorArray[index] = newColor;
52    }
53  
54    return results;
55}
56

Time and Space Complexity

Time Complexity: O(m) where m is the length of queries.

The algorithm iterates through each query exactly once. For each query, it performs a constant number of operations:

  • Checking and updating at most 2 adjacent positions (left and right neighbors)
  • Each check involves simple comparisons and arithmetic operations (O(1))
  • Updating the array at position i and the answer array

Since we process m queries and each query takes O(1) time, the total time complexity is O(m).

Space Complexity: O(n + m) where n is the size of the array and m is the length of queries.

The space usage consists of:

  • nums array: O(n) space to store the current state of the array
  • ans array: O(m) space to store the result for each query
  • Variable x: O(1) space for tracking the current count of adjacent same-color pairs
  • Loop variables: O(1) space

The total auxiliary space used is O(n + m). If we only consider extra space beyond the output array ans, then the auxiliary space complexity would be O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Updating the Array Before Adjusting the Count

One of the most common mistakes is updating colors[position] = new_color before calculating the changes to the adjacent pair count. This destroys the information about the previous color, making it impossible to correctly determine which pairs need to be removed from the count.

Incorrect approach:

# WRONG: Updating color first
colors[position] = new_color  # This loses the old color information!

# Now we can't properly check what pairs existed before
if position > 0 and old_color_we_lost == colors[position - 1]:
    adjacent_same_color_count -= 1

Correct approach:

# RIGHT: Calculate changes first, then update
# Check and remove old pairs
if position > 0 and colors[position] != 0 and colors[position - 1] == colors[position]:
    adjacent_same_color_count -= 1

# ... other checks ...

# Update color AFTER all calculations
colors[position] = new_color

2. Forgetting to Check for Zero (Uncolored) Values

Another critical mistake is not properly handling uncolored elements (value 0). Uncolored elements should never contribute to the matching pair count, even if two adjacent elements are both 0.

Incorrect approach:

# WRONG: Not checking if color is 0
if position > 0 and colors[position - 1] == new_color:
    adjacent_same_color_count += 1  # This would count pairs of zeros!

Correct approach:

# RIGHT: Ensure we're not counting zero-colored pairs
if position > 0 and colors[position - 1] == new_color and new_color != 0:
    adjacent_same_color_count += 1

3. Double Counting or Missing Pairs When a Position is Recolored

When a position that already has a color is being recolored with the same color, you need to be careful not to double-count existing pairs or incorrectly remove pairs that should remain.

Example scenario: If position i has color 2, and both neighbors also have color 2, recoloring position i with color 2 again shouldn't change the count. The implementation handles this correctly by first removing the existing pairs (subtracting 2) and then adding them back (adding 2), resulting in no net change.

4. Incorrect Boundary Checking

Failing to properly check array boundaries can lead to index out of bounds errors or incorrect counts at the edges of the array.

Incorrect approach:

# WRONG: Not checking boundaries
if colors[position - 1] == colors[position]:  # Could fail when position = 0
    adjacent_same_color_count -= 1

Correct approach:

# RIGHT: Always check boundaries first
if position > 0 and colors[position - 1] == colors[position]:
    adjacent_same_color_count -= 1
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Given a sorted array of integers and an integer called target, find the element that equals to the target and return its index. Select the correct code that fills the ___ in the given code snippet.

1def binary_search(arr, target):
2    left, right = 0, len(arr) - 1
3    while left ___ right:
4        mid = (left + right) // 2
5        if arr[mid] == target:
6            return mid
7        if arr[mid] < target:
8            ___ = mid + 1
9        else:
10            ___ = mid - 1
11    return -1
12
1public static int binarySearch(int[] arr, int target) {
2    int left = 0;
3    int right = arr.length - 1;
4
5    while (left ___ right) {
6        int mid = left + (right - left) / 2;
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16
1function binarySearch(arr, target) {
2    let left = 0;
3    let right = arr.length - 1;
4
5    while (left ___ right) {
6        let mid = left + Math.trunc((right - left) / 2);
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16

Recommended Readings

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

Load More