Alternating Groups III solution and explanation
2 more videos on this problem
The problem
There are some red and blue tiles arranged circularly. You are given an array of integers colors and a 2D integers array queries.
The color of tile i is represented by colors[i]:
colors[i] == 0means that tileiis red.colors[i] == 1means that tileiis blue.
An alternating group is a contiguous subset of tiles in the circle with alternating colors (each tile in the group except the first and last one has a different color from its adjacent tiles in the group).
You have to process queries of two types:
queries[i] = [1, sizei], determine the count of alternating groups with sizesizei.queries[i] = [2, indexi, colori], changecolors[indexi]tocolori.
Return an array answer containing the results of the queries of the first type in order.
Note that since colors represents a circle, the first and the last tiles are considered to be next to each other.
Example 1:
Input: colors = [0,1,1,0,1], queries = [[2,1,0],[1,4]]
Output: [2]
Explanation:
First query:
Change colors[1] to 0.

Second query:
Count of the alternating groups with size 4:


Example 2:
Input: colors = [0,0,1,0,1,1], queries = [[1,3],[2,3,0],[1,5]]
Output: [2,0]
Explanation:

First query:
Count of the alternating groups with size 3:


Second query: colors will not change.
Third query: There is no alternating group with size 5.
Constraints:
4 <= colors.length <= 5 * 1040 <= colors[i] <= 11 <= queries.length <= 5 * 104queries[i][0] == 1orqueries[i][0] == 2- For all
ithat:queries[i][0] == 1:queries[i].length == 2,3 <= queries[i][1] <= colors.length - 1queries[i][0] == 2:queries[i].length == 3,0 <= queries[i][1] <= colors.length - 1,0 <= queries[i][2] <= 1
Approach overview
Problem Overview: You are given a circular array of colors and a sequence of queries. Some queries update the color at an index, while others ask how many alternating groups of a given size exist in the array. An alternating group means adjacent elements strictly alternate (e.g., 0,1,0,1). Because updates modify the structure dynamically, the solution must efficiently maintain alternating segments while answering queries quickly.
Approach 1: Recursive Approach with Memoization (Brute-Force Simulation) (Time: O(n * q), Space: O(n))
The most direct method recomputes alternating segments whenever a query asks for the number of valid groups. Traverse the array and recursively check whether consecutive elements alternate, storing intermediate results with memoization to avoid recomputing overlapping segments. For each query, scan the array and count segments whose length satisfies the requested size. This works for small inputs but becomes slow when updates and queries are frequent because each query may require scanning most of the array.
Approach 2: Dynamic Programming with Binary Indexed Tree (Optimal) (Time: O((n + q) log n), Space: O(n))
The efficient strategy tracks maximal alternating segments instead of recomputing them from scratch. First, determine whether each adjacent pair alternates. This transforms the problem into maintaining runs of valid alternating edges. When a color update occurs, only the two neighboring relationships change, so the affected segments can be split or merged locally. Use a Binary Indexed Tree to store counts of segment lengths and support prefix queries for "how many segments have length ≥ k".
Dynamic programming ideas help maintain the length of alternating runs starting at each index. When an update breaks or forms alternation, adjust the boundaries of the affected segments and update their lengths in the Fenwick tree. Each modification touches only a constant number of segments, keeping updates efficient. Query operations then become simple range queries over segment lengths using the BIT.
This approach works well because it converts a structural property of the array into a frequency problem that a Fenwick tree handles efficiently. The array itself is still accessed directly for updates, while the BIT maintains aggregate statistics about segment sizes.
Recommended for interviews: The Binary Indexed Tree solution is what interviewers expect for a hard problem with dynamic updates. The brute-force or recursive simulation demonstrates you understand how alternating groups are formed, but the optimized structure shows you can maintain derived properties under updates. Strong candidates recognize that only local relationships change after an update and use data structures like a array plus Fenwick tree or segment tree to maintain counts efficiently. Concepts from dynamic programming help track alternating run lengths, while the BIT enables fast aggregated queries.
Approach 1: Dynamic Programming Approach
This approach leverages dynamic programming to solve the problem by breaking it down into subproblems and storing their solutions for reuse. It is beneficial in cases where the problem exhibits overlapping subproblems and optimal substructure properties. By using a table to keep track of solutions to subproblems, this approach reduces the time complexity significantly compared to naive methods.
def solveProblem(n):
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
result = solveProblem(10)
print("Result:", result)public class Solution {
public static int solveProblem(int n) {
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
public static void main(String[] args) {
int result = solveProblem(10);
System.out.println("Result: " + result);
}
}#include <iostream>
#include <vector>
int solveProblem(int n) {
std::vector<int> dp(n+1, 0);
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; ++i) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
int main() {
int result = solveProblem(10);
std::cout << "Result: " << result << std::endl;
return 0;
}function solveProblem(n) {
const dp = Array(n + 1).fill(0);
dp[0] = 1;
dp[1] = 1;
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
const result = solveProblem(10);
console.log(`Result: ${result}`);#include <stdio.h>
#define MAX 100
int solveProblem(int n) {
int dp[MAX];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
int main() {
int result = solveProblem(10);
printf("Result: %d\n", result);
return 0;
}using System;
class Solution {
static int SolveProblem(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
static void Main() {
int result = SolveProblem(10);
Console.WriteLine("Result: " + result);
}
}In Python, a list is used to implement dynamic programming. We allocate space for the list and fill it with values from the base cases up to the target.
Time Complexity: O(n).
Space Complexity: O(n) for the list containing subproblem solutions.
Approach 2: Recursive Approach with Memoization
This approach uses a recursive function to solve the problem, complemented by memoization to store previously calculated results. By caching the results of function calls, this method combines the simplicity of recursive solutions with the efficiency of iterative dynamic programming, thus reducing redundant computations drastically.
def solveProblem(n, dp):
if n <= 1:
return 1
if dp[n] != -1:
return dp[n]
dp[n] = solveProblem(n - 1, dp) + solveProblem(n - 2, dp)
return dp[n]
dp = [-1] * 101
result = solveProblem(10, dp)
print("Result:", result)import java.util.Arrays;
public class Solution {
static int[] dp;
public static int solveProblem(int n) {
if (n <= 1) return 1;
if (dp[n] != -1) return dp[n];
return dp[n] = solveProblem(n - 1) + solveProblem(n - 2);
}
public static void main(String[] args) {
dp = new int[101];
Arrays.fill(dp, -1);
int result = solveProblem(10);
System.out.println("Result: " + result);
}
}#include <iostream>
#include <vector>
using namespace std;
vector<int> dp;
int solveProblem(int n) {
if (n <= 1) return 1;
if (dp[n] != -1) return dp[n];
return dp[n] = solveProblem(n - 1) + solveProblem(n - 2);
}
int main() {
dp.resize(101, -1);
int result = solveProblem(10);
cout << "Result: " << result << endl;
return 0;
}function solveProblem(n, dp) {
if (n <= 1) return 1;
if (dp[n] !== -1) return dp[n];
dp[n] = solveProblem(n - 1, dp) + solveProblem(n - 2, dp);
return dp[n];
}
const dp = Array(101).fill(-1);
const result = solveProblem(10, dp);
console.log(`Result: ${result}`);#include <stdio.h>
#include <string.h>
int dp[100];
int solveProblem(int n) {
if (n <= 1) return 1;
if (dp[n] != -1) return dp[n];
return dp[n] = solveProblem(n - 1) + solveProblem(n - 2);
}
int main() {
memset(dp, -1, sizeof(dp));
int result = solveProblem(10);
printf("Result: %d\n", result);
return 0;
}using System;
class Solution {
static int[] dp = new int[101];
static int SolveProblem(int n) {
if (n <= 1) return 1;
if (dp[n] != -1) return dp[n];
return dp[n] = SolveProblem(n - 1) + SolveProblem(n - 2);
}
static void Main() {
Array.Fill(dp, -1);
int result = SolveProblem(10);
Console.WriteLine("Result: " + result);
}
}A Python function implementing recursion with memoization. A list dp holds memoized values, thus preventing expensive repeated calculations.
Time Complexity: O(n).
Space Complexity: O(n), attributed to both recursion stack and memoization list.
Complexity comparison
| Approach | Complexity |
|---|---|
| Dynamic Programming Approach | Time Complexity: O(n). |
| Recursive Approach with Memoization | Time Complexity: O(n). |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Recursive Simulation with Memoization | O(n * q) | O(n) | Good for understanding how alternating groups form; feasible only when constraints are small |
| Dynamic Programming + Binary Indexed Tree | O((n + q) log n) | O(n) | Best for large inputs with frequent updates and queries; maintains segment counts efficiently |
Frequently asked questions
Is Alternating Groups III easy or hard?
Alternating Groups III is classified as Hard with a low acceptance rate around 19%. The difficulty comes from handling dynamic updates while maintaining alternating segment information efficiently using advanced data structures like Fenwick trees.
What is the best approach for Alternating Groups III?
The most efficient approach maintains alternating segments dynamically using a Binary Indexed Tree (Fenwick tree). Instead of recomputing groups after every update, track segment lengths and update only the affected boundaries when a color changes. Each update and query runs in O(log n) time.
Is Alternating Groups III asked at Google/Amazon/Meta?
Problems involving dynamic arrays, Fenwick trees, and segment maintenance appear frequently in interviews at companies like Google, Amazon, and Meta. Alternating Groups III reflects the same pattern: maintain structural properties under updates and answer queries efficiently.
What data structure is used in Alternating Groups III?
The key data structure is a Binary Indexed Tree (Fenwick tree), used to maintain counts of alternating segment lengths and support fast prefix queries. The underlying array stores colors, while the tree tracks aggregated statistics for efficient queries.
What is the time complexity of Alternating Groups III?
The optimized solution using a Binary Indexed Tree runs in O((n + q) log n) time, where n is the number of elements and q is the number of queries. Each update modifies only nearby relationships and updates the Fenwick tree in O(log n). Space complexity is O(n).
Alternating Groups III Python or Java solution approach?
In Python or Java, implement a Fenwick tree class that supports update and prefix-sum operations. Maintain alternating segment boundaries in arrays or sets, update them when a color changes, and update the Fenwick tree with the new segment lengths. Each operation runs in O(log n).
How to solve Alternating Groups III in O(log n) per query?
Track maximal alternating segments and store their lengths in a Fenwick tree. When a color update occurs, only the two adjacent relationships may change, which can split or merge segments. Update those segment lengths in the tree, and answer queries by counting segments whose length satisfies the required size using prefix sums.



