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
.
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 includingnums[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 subarrayss
: Tracks the number of alternating subarrays ending at the current position- Initialize both
ans
ands
to1
since the first element forms one valid subarray
Algorithm Steps:
-
Start with the first element: Set
s = 1
because there's exactly one alternating subarray ending at position 0 (the single element itself). -
Iterate through adjacent pairs: Use
pairwise(nums)
to examine each pair of consecutive elements(a, b)
wherea = nums[i-1]
andb = nums[i]
. -
Update the count for current position:
- If
a != b
(elements alternate): Sets = s + 1
- This means all
s
subarrays ending at the previous position can be extended - Plus one new subarray starting at the current position
- This means all
- If
a == b
(elements are the same): Resets = 1
- The alternating pattern breaks
- Only the single element at current position forms a valid subarray
- If
-
Accumulate the result: Add
s
toans
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
, sos = 2
,ans = 1 + 2 = 3
(subarrays:[1]
,[0,1]
) - Position 2:
1 == 1
, sos = 1
,ans = 3 + 1 = 4
(subarray:[1]
) - Position 3:
1 == 1
, sos = 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 EvaluatorExample 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.
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!