Facebook Pixel

3101. Count Alternating Subarrays

Problem Description

You are given a binary array nums containing only 0s and 1s.

An alternating subarray is defined as a subarray where no two adjacent elements have the same value. In other words, the values must alternate between 0 and 1 (like [0,1,0,1] or [1,0,1]).

Your task is to count the total number of alternating subarrays in the given array nums.

For example:

  • In array [0,1,0,1], all possible subarrays are alternating: [0], [1], [0], [1], [0,1], [1,0], [0,1], [0,1,0], [1,0,1], and [0,1,0,1]
  • In array [0,0,1], the alternating subarrays would be: [0] (first), [0] (second), [1], and [0,1] - but not [0,0] or [0,0,1] since they have adjacent equal elements

The solution uses a dynamic approach where for each position i, it tracks how many alternating subarrays end at that position. If nums[i] differs from nums[i-1], we can extend all alternating subarrays ending at i-1 by one element, plus the single-element subarray [nums[i]]. If they're equal, we can only have the single-element subarray ending at position i.

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

Intuition

The key insight is to think about how alternating subarrays grow as we traverse the array. When we're at position i, we need to count all alternating subarrays that end at this position.

Let's think step by step. If we have an alternating subarray ending at position i-1, when can we extend it to position i? Only when nums[i] != nums[i-1]. In this case:

  • All alternating subarrays ending at i-1 can be extended by including nums[i]
  • Plus, we get a new single-element subarray [nums[i]]

For example, if we have alternating subarrays [1] and [0,1] ending at position i-1 where nums[i-1] = 1, and nums[i] = 0, then we get:

  • Extended subarrays: [1,0] and [0,1,0]
  • New single-element: [0]
  • Total: 3 subarrays ending at position i

However, if nums[i] == nums[i-1], we break the alternating pattern. We can't extend any previous alternating subarray. We can only start fresh with the single-element subarray [nums[i]].

This leads to a pattern: if we track the count s of alternating subarrays ending at each position:

  • When elements alternate: s = s + 1 (previous count plus one for the new single element)
  • When elements are same: s = 1 (reset to just the single element)

By accumulating these counts for every position in the array, we get the total number of alternating subarrays. This works because every alternating subarray must end at some position, and we're counting them all exactly once.

Learn more about Math patterns.

Solution Approach

The implementation follows a dynamic programming pattern where we track the count of alternating subarrays ending at each position.

Variables:

  • ans: Accumulates the total count of alternating subarrays
  • s: Tracks the number of alternating subarrays ending at the current position
  • Initialize both ans and s to 1 since the first element forms one valid subarray

Algorithm Steps:

  1. Start with the first element: Set s = 1 because there's exactly one alternating subarray ending at position 0 (the single element itself).

  2. Iterate through adjacent pairs: Use pairwise(nums) to examine each pair of consecutive elements (a, b) where a = nums[i-1] and b = nums[i].

  3. Update the count for current position:

    • If a != b (elements alternate): Set s = s + 1
      • This means all s subarrays ending at the previous position can be extended
      • Plus one new subarray starting at the current position
    • If a == b (elements are the same): Reset s = 1
      • The alternating pattern breaks
      • Only the single element at current position forms a valid subarray
  4. Accumulate the result: Add s to ans after each update. This ensures we count all alternating subarrays ending at each position.

Example walkthrough with nums = [0,1,1,1]:

  • Position 0: s = 1, ans = 1 (subarray: [0])
  • Position 1: 0 != 1, so s = 2, ans = 1 + 2 = 3 (subarrays: [1], [0,1])
  • Position 2: 1 == 1, so s = 1, ans = 3 + 1 = 4 (subarray: [1])
  • Position 3: 1 == 1, so s = 1, ans = 4 + 1 = 5 (subarray: [1])

The time complexity is O(n) as we traverse the array once, and space complexity is O(1) as we only use a constant amount of extra space.

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 the solution with nums = [1,0,1,0,0] to see how we count alternating subarrays.

Initial Setup:

  • Start with s = 1 (count of alternating subarrays ending at position 0)
  • Initialize ans = 1 (total count starts with the first element [1])

Step-by-step Processing:

Position 1: Compare nums[0]=1 with nums[1]=0

  • Since 1 ≠ 0 (they alternate), we can extend previous subarrays
  • Update: s = s + 1 = 2
  • This represents 2 subarrays ending at position 1:
    • [0] (new single element)
    • [1,0] (extended from position 0)
  • Add to total: ans = 1 + 2 = 3

Position 2: Compare nums[1]=0 with nums[2]=1

  • Since 0 ≠ 1 (they alternate), extend again
  • Update: s = s + 1 = 3
  • This represents 3 subarrays ending at position 2:
    • [1] (new single element)
    • [0,1] (extended from [0])
    • [1,0,1] (extended from [1,0])
  • Add to total: ans = 3 + 3 = 6

Position 3: Compare nums[2]=1 with nums[3]=0

  • Since 1 ≠ 0 (they alternate), extend again
  • Update: s = s + 1 = 4
  • This represents 4 subarrays ending at position 3:
    • [0] (new single element)
    • [1,0] (extended from [1])
    • [0,1,0] (extended from [0,1])
    • [1,0,1,0] (extended from [1,0,1])
  • Add to total: ans = 6 + 4 = 10

Position 4: Compare nums[3]=0 with nums[4]=0

  • Since 0 = 0 (same values), the alternating pattern breaks
  • Reset: s = 1
  • This represents only 1 subarray ending at position 4:
    • [0] (can only start fresh with single element)
  • Add to total: ans = 10 + 1 = 11

Final Result: The array [1,0,1,0,0] contains 11 alternating subarrays.

The key insight: when elements alternate, we build upon previous alternating subarrays (s = s + 1). When they're the same, we start fresh (s = 1). By accumulating these counts at each position, we capture all possible alternating subarrays in the array.

Solution Implementation

1class Solution:
2    def countAlternatingSubarrays(self, nums: List[int]) -> int:
3        """
4        Count the total number of alternating subarrays.
5        An alternating subarray is one where adjacent elements are different.
6        """
7        # Total count of alternating subarrays
8        total_count = 1
9      
10        # Length of current alternating subarray ending at current position
11        current_length = 1
12      
13        # Iterate through adjacent pairs of elements
14        for i in range(1, len(nums)):
15            if nums[i] != nums[i-1]:
16                # Extend the current alternating subarray
17                current_length += 1
18            else:
19                # Reset to a new alternating subarray of length 1
20                current_length = 1
21          
22            # Add the count of alternating subarrays ending at current position
23            # (there are 'current_length' such subarrays)
24            total_count += current_length
25      
26        return total_count
27
1class Solution {
2    public long countAlternatingSubarrays(int[] nums) {
3        // Total count of alternating subarrays
4        long totalCount = 1;
5      
6        // Current length of alternating sequence ending at current position
7        long currentSequenceLength = 1;
8      
9        // Iterate through the array starting from the second element
10        for (int i = 1; i < nums.length; i++) {
11            // Check if current element is different from previous element
12            if (nums[i] != nums[i - 1]) {
13                // Extend the current alternating sequence
14                currentSequenceLength = currentSequenceLength + 1;
15            } else {
16                // Reset sequence length as alternation is broken
17                currentSequenceLength = 1;
18            }
19          
20            // Add the number of alternating subarrays ending at index i
21            // This equals the length of the current alternating sequence
22            totalCount += currentSequenceLength;
23        }
24      
25        return totalCount;
26    }
27}
28
1class Solution {
2public:
3    long long countAlternatingSubarrays(vector<int>& nums) {
4        // Total count of alternating subarrays
5        long long totalCount = 1;
6      
7        // Length of current alternating subarray ending at current position
8        long long currentLength = 1;
9      
10        // Iterate through the array starting from the second element
11        for (int i = 1; i < nums.size(); ++i) {
12            // Check if current element is different from previous element
13            if (nums[i] != nums[i - 1]) {
14                // Extend the current alternating subarray
15                currentLength = currentLength + 1;
16            } else {
17                // Reset to new alternating subarray starting at current position
18                currentLength = 1;
19            }
20          
21            // Add the count of alternating subarrays ending at position i
22            // This equals the length of the current alternating sequence
23            totalCount += currentLength;
24        }
25      
26        return totalCount;
27    }
28};
29
1/**
2 * Counts the total number of alternating subarrays in the given array.
3 * An alternating subarray is one where adjacent elements are different.
4 * 
5 * @param nums - The input array of numbers
6 * @returns The total count of alternating subarrays
7 */
8function countAlternatingSubarrays(nums: number[]): number {
9    // Initialize total count and current alternating sequence length
10    let totalCount: number = 1;
11    let currentSequenceLength: number = 1;
12  
13    // Iterate through the array starting from the second element
14    for (let i: number = 1; i < nums.length; i++) {
15        // If current element differs from previous, extend the sequence
16        // Otherwise, reset the sequence length to 1
17        if (nums[i] !== nums[i - 1]) {
18            currentSequenceLength = currentSequenceLength + 1;
19        } else {
20            currentSequenceLength = 1;
21        }
22      
23        // Add the current sequence length to total count
24        // This counts all alternating subarrays ending at index i
25        totalCount = totalCount + currentSequenceLength;
26    }
27  
28    return totalCount;
29}
30

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. This is because the algorithm uses pairwise(nums) to iterate through consecutive pairs of elements in the array exactly once. The pairwise function generates n-1 pairs for an array of length n, and each iteration performs constant-time operations (comparison, conditional assignment, and addition).

The space complexity is O(1). The algorithm only uses a fixed amount of extra space for variables ans and s, regardless of the input size. The pairwise iterator doesn't create a new data structure but rather generates pairs on-the-fly, maintaining constant space usage throughout the execution.

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

Common Pitfalls

1. Off-by-One Error in Initialization

A common mistake is incorrectly initializing the variables, particularly when developers think they need to handle the first element separately after the loop starts.

Incorrect approach:

def countAlternatingSubarrays(self, nums: List[int]) -> int:
    total_count = 0  # Wrong: Missing the first element
    current_length = 0  # Wrong: Should start at 1
  
    for i in range(1, len(nums)):
        if nums[i] != nums[i-1]:
            current_length += 1
        else:
            current_length = 1
        total_count += current_length
  
    return total_count  # This misses counting the first element

Why it's wrong: This approach forgets to count the single-element subarray at position 0.

Correct fix: Initialize total_count = 1 and current_length = 1 to account for the first element forming one valid subarray.

2. Misunderstanding What current_length Represents

Developers might confuse current_length as the length of the longest alternating subarray seen so far, rather than the count of alternating subarrays ending at the current position.

Incorrect interpretation:

def countAlternatingSubarrays(self, nums: List[int]) -> int:
    max_length = 1  # Wrong variable name/concept
    total_count = 1
  
    for i in range(1, len(nums)):
        if nums[i] != nums[i-1]:
            max_length = max(max_length, i + 1)  # Wrong logic
        total_count += 1  # Wrong: Always adding 1
  
    return total_count

Why it's wrong: The variable tracks the wrong metric. We need the count of subarrays ending at position i, not the maximum length.

Correct understanding: current_length represents how many valid alternating subarrays end at the current position. For example, if current_length = 3 at position i, it means there are 3 alternating subarrays ending at i: one of length 3, one of length 2, and one of length 1.

3. Edge Case: Empty or Single-Element Arrays

While the provided solution handles single-element arrays correctly, developers might add unnecessary special case handling that complicates the code.

Overcomplicated approach:

def countAlternatingSubarrays(self, nums: List[int]) -> int:
    if not nums:  # Unnecessary if problem guarantees non-empty
        return 0
    if len(nums) == 1:  # Unnecessary special case
        return 1
  
    # Rest of the logic...

Better approach: The main algorithm naturally handles single-element arrays since the loop simply won't execute when len(nums) == 1, and total_count = 1 is already the correct answer.

4. Using Wrong Comparison for Alternation

Some might accidentally check for specific values rather than inequality:

Incorrect:

if nums[i] == 1 and nums[i-1] == 0:  # Wrong: Only checks one pattern
    current_length += 1

Correct: Use if nums[i] != nums[i-1]: to check for alternation regardless of whether the pattern is 0-1-0 or 1-0-1.

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

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

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

Load More