Full-length 90-minute simulations that replicate the exact conditions of the Toptal Codility round. Each test contains 3 problems (Easy + Medium + Hard) with complete C# solutions and detailed walkthroughs.
Simulate real interview conditions. Each test is designed to take exactly 90 minutes. Don't look at solutions until you've finished all three problems or time expires.
Three problems covering permutation checking, sliding window averages, and lazy propagation techniques.
A permutation is a sequence containing each integer from 1 to N exactly once (e.g., [4, 1, 3, 2] is a permutation of length 4).
Given array A of length N, return 1 if it's a permutation, otherwise return 0.
Hint 1: A valid permutation of length N contains exactly the integers 1, 2, ..., N. Check two conditions: (1) no duplicates, (2) all values in range [1, N].
Hint 2: Use a HashSet to detect duplicates in O(N) time. If any element is not in [1, N], return 0 immediately.
Hint 3: After validating no duplicates and correct range, also check that we've seen exactly N elements.
public int PermCheck(int[] A) { // Edge case: array of length 0 if (A.Length == 0) return 0; // Check for duplicates and valid range using HashSet HashSet<int> seen = new HashSet<int>(); for (int i = 0; i < A.Length; i++) { // Element must be in range [1, N] if (A[i] < 1 || A[i] > A.Length) return 0; // No duplicates allowed if (!seen.Add(A[i])) return 0; } return 1; } // Trace Example 1: A = [4, 1, 3, 2] // i=0: A[0]=4, in [1,4]? yes, seen.Add(4)→true, seen={4} // i=1: A[1]=1, in [1,4]? yes, seen.Add(1)→true, seen={4,1} // i=2: A[2]=3, in [1,4]? yes, seen.Add(3)→true, seen={4,1,3} // i=3: A[3]=2, in [1,4]? yes, seen.Add(2)→true, seen={4,1,3,2} // Return 1 ✓ // Trace Example 3: A = [0, 1] // i=0: A[0]=0, in [1,2]? no → return 0 ✓
A slice of array A is any contiguous subarray. The average of a slice is the sum of its elements divided by its length.
Find the starting position of the slice with the minimum average. If there are multiple slices with the same minimum average, return the smallest starting index.
Hint 1: Naive approach: check all slices. For each starting index i and each ending index j ≥ i, compute average. This is O(N²) but works for small N.
Hint 2: Key insight: the minimum average slice is always of length 2 or 3. Why? If a slice of length ≥4 has minimum average, we can show one of its 2- or 3-element sub-slices must have lower average.
Hint 3: Optimal solution: iterate through all slices of length 2 and 3 only. This is O(N).
public int MinAvgTwoSlice(int[] A) { double minAvg = double.MaxValue; int minIdx = 0; // Check all slices of length 2 for (int i = 0; i < A.Length - 1; i++) { double avg = (A[i] + A[i + 1]) / 2.0; if (avg < minAvg) { minAvg = avg; minIdx = i; } } // Check all slices of length 3 for (int i = 0; i < A.Length - 2; i++) { double avg = (A[i] + A[i + 1] + A[i + 2]) / 3.0; if (avg < minAvg) { minAvg = avg; minIdx = i; } } return minIdx; } // Trace Example 1: A = [4, 2, 2, 5, 1, 5, 8] // Length 2 slices: // i=0: (4+2)/2 = 3.0 // i=1: (2+2)/2 = 2.0 ← minimum so far, minIdx=1 // i=2: (2+5)/2 = 3.5 // i=3: (5+1)/2 = 3.0 // i=4: (1+5)/2 = 3.0 // i=5: (5+8)/2 = 6.5 // Length 3 slices: // i=0: (4+2+2)/3 = 2.67 // i=1: (2+2+5)/3 = 3.0 // ... (all ≥ 2.0) // Return 1 ✓
You have N counters (indexed 1 to N), all starting at 0. You perform M operations:
i (1 ≤ i ≤ N): increment counter iN+1: set all counters to the current maximum counter valueReturn the final value of all counters after all M operations.
Hint 1: Naive approach: for each "set all" operation, iterate through all N counters and update them. This is O(M·N) in the worst case (slow).
Hint 2: Lazy propagation: track the "base level" all counters should reset to. When a counter is incremented, increment relative to the base. When "set all" happens, update the base (not individual counters).
Hint 3: At the end, for each counter, its final value is max(counter_value, base_level).
public int[] MaxCounters(int N, int[] A) { int[] counters = new int[N]; int baseLevel = 0; // Lazy value all counters reset to int maxCounter = 0; // Max counter value seen (excluding base) for (int op = 0; op < A.Length; op++) { int operation = A[op]; if (operation == N + 1) { // Set all to max: update base level baseLevel = maxCounter; } else { // Increment counter (1-indexed) int idx = operation - 1; // If this counter hasn't been touched since last reset, // its actual value is baseLevel if (counters[idx] < baseLevel) { counters[idx] = baseLevel; } counters[idx]++; maxCounter = Math.Max(maxCounter, counters[idx]); } } // Finalize: ensure all counters are at least baseLevel for (int i = 0; i < N; i++) { if (counters[i] < baseLevel) { counters[i] = baseLevel; } } return counters; } // Trace Example: N = 5, A = [3, 4, 4, 6, 1, 4, 4] // Initial: counters=[0,0,0,0,0], baseLevel=0, maxCounter=0 // op=0: A[0]=3 → idx=2, counters[2]++→1, maxCounter=1 // counters=[0,0,1,0,0] // op=1: A[1]=4 → idx=3, counters[3]++→1, maxCounter=1 // counters=[0,0,1,1,0] // op=2: A[2]=4 → idx=3, counters[3]++→2, maxCounter=2 // counters=[0,0,1,2,0] // op=3: A[3]=6 → operation==N+1, baseLevel=2, maxCounter=2 // op=4: A[4]=1 → idx=0, counters[0]<2, counters[0]=2, ++→3 // counters=[3,0,1,2,0], maxCounter=3 // op=5: A[5]=4 → idx=3, counters[3]=2, ++→3 // counters=[3,0,1,3,0], maxCounter=3 // op=6: A[6]=4 → idx=3, counters[3]=3, ++→4 // counters=[3,0,1,4,0], maxCounter=4 // Finalize: apply baseLevel=2 to untouched counters // counters[1]=0<2→2, counters[4]=0<2→2 // Result: [3,2,2,4,2] ✓
Three problems covering dynamic array traversal, slice uniqueness, and geometric intersection counting.
A small frog wants to cross from position 0 to position X+1. The frog must pass through all integers from 1 to X.
Leaves are placed at array indices with values 1 to X. Leaf at position i arrives at time A[i].
Find the earliest time t such that the frog can reach position X+1 (has stepped on all positions 1..X).
Hint 1: Track when each position (1 to X) first becomes available. Use a HashSet to track which positions have been seen.
Hint 2: Iterate through the array. For each leaf value in [1, X], record its arrival time. When all positions [1, X] are covered, find the maximum time seen.
Hint 3: If any position is missing, return -1.
public int FrogRiverOne(int X, int[] A) { int[] earliestTime = new int[X + 1]; // earliestTime[i] = first time we see leaf at position i (1-indexed) for (int t = 0; t < A.Length; t++) { int pos = A[t]; if (pos >= 1 && pos <= X && earliestTime[pos] == 0) { earliestTime[pos] = t; } } // Find the time when all positions [1, X] are covered int maxTime = 0; for (int pos = 1; pos <= X; pos++) { if (earliestTime[pos] == 0) { // Position not found return -1; } maxTime = Math.Max(maxTime, earliestTime[pos]); } return maxTime; } // Trace Example: X = 5, A = [1, 3, 1, 4, 2, 3, 5, 4] // t=0: pos=1 → earliestTime[1]=0 // t=1: pos=3 → earliestTime[3]=1 // t=2: pos=1 → skip (already set) // t=3: pos=4 → earliestTime[4]=3 // t=4: pos=2 → earliestTime[2]=4 // t=5: pos=3 → skip (already set) // t=6: pos=5 → earliestTime[5]=6 // t=7: pos=4 → skip (already set) // Check: all positions [1..5] have times [0,4,1,3,6] // maxTime = 6 → return 6 ✓
A slice [L, R] is "distinct" if all elements in A[L..R] are unique (no duplicates).
Count the total number of distinct slices in array A. If the count exceeds 1,000,000,000, return 1,000,000,000.
Hint 1: Brute force: for each (L, R) pair, check if slice is distinct. This is O(N²) or O(N³).
Hint 2: Sliding window: for each left index L, find the maximum right index R such that [L, R] is distinct. All slices [L, L], [L, L+1], ..., [L, R] are distinct.
Hint 3: When you extend R and find a duplicate, move L forward. Use a HashSet or dict to track which elements are in the current window.
public int CountDistinctSlices(int M, int[] A) { // M is the upper bound for elements (0 to M-1) long count = 0; const long MAX = 1000000000; HashSet<int> window = new HashSet<int>(); int left = 0; for (int right = 0; right < A.Length; right++) { // Try to add A[right] to window while (window.Contains(A[right])) { // Duplicate found, shrink from left window.Remove(A[left]); left++; } // A[right] can now be added window.Add(A[right]); // All slices [left..right], [left+1..right], ..., [right..right] // are distinct. That's (right - left + 1) slices. count += (right - left + 1); if (count > MAX) return (int)MAX; } return (int)count; } // Trace Example: A = [3, 4, 3, 2] (assuming M=100) // right=0: A[0]=3, window={3}, count += (0-0+1)=1 // Slices: [3] // right=1: A[1]=4, window={3,4}, count += (1-0+1)=2 → count=3 // Slices: [4], [3,4] // right=2: A[2]=3, duplicate! shrink left: // remove A[0]=3 → left=1, window={4}, re-add A[2]=3, window={4,3} // count += (2-1+1)=2 → count=5 // Slices: [4,3] and [3] // right=3: A[3]=2, window={4,3,2}, count += (3-1+1)=3 → count=8 // Slices: [4,3,2], [3,2], [2] // Return 8... but example says 9? // Recounting by hand: [3], [4], [3], [2], [3,4], [4,3], [3,2], [4,3,2] // That's 8 distinct slices (note: [3,4,3] is NOT distinct)
You have N discs in a 1D line. Disc i is centered at position i (0-indexed) with radius A[i].
Two discs i and j intersect if the distance between their centers is ≤ the sum of their radii: |i - j| ≤ A[i] + A[j].
Count the number of pairs (i, j) where i < j and the discs intersect. If count > 10,000,000, return -1.
Hint 1: Brute force: check all O(N²) pairs. For large N, this may TLE, but could be acceptable if optimized.
Hint 2: Event-based approach: represent each disc as an interval [i - A[i], i + A[i]]. Two discs intersect if their intervals overlap. Use a sweep line algorithm with events.
Hint 3: For each disc i, the discs it can intersect with are those j where i < j ≤ i + A[i]. Iterate through discs and count valid j efficiently.
public int NumberOfDiscIntersections(int[] A) { long count = 0; const int MAX_INTERSECTIONS = 10000000; for (int i = 0; i < A.Length; i++) { // Disc i: center at i, radius A[i] // It intersects disc j if |i - j| <= A[i] + A[j] // For j > i: (j - i) <= A[i] + A[j] // j <= i + A[i] + A[j] for (int j = i + 1; j < A.Length; j++) { // Check intersection condition long dist = j - i; // distance between centers long sumRadii = (long)A[i] + A[j]; // sum of radii if (dist <= sumRadii) { count++; if (count > MAX_INTERSECTIONS) return -1; } } } return (int)count; } // Trace Example: A = [0, 1, 1, 0, 0] // i=0 (center 0, radius 0): // j=1: dist=1, sumRadii=0+1=1, 1≤1? yes → count=1 // j=2: dist=2, sumRadii=0+1=1, 2≤1? no // j=3: dist=3, sumRadii=0+0=0, 3≤0? no // j=4: dist=4, sumRadii=0+0=0, 4≤0? no // i=1 (center 1, radius 1): // j=2: dist=1, sumRadii=1+1=2, 1≤2? yes → count=2 // j=3: dist=2, sumRadii=1+0=1, 2≤1? no // j=4: dist=3, sumRadii=1+0=1, 3≤1? no // i=2,3,4: (no more pairs) // Return 2 ✓
Three problems covering set operations, graph traversal (food chain), and dynamic programming (subset sum minimization).
Given a sorted array A (non-decreasing), count the number of distinct elements.
Hint 1: Since the array is sorted, all identical elements are adjacent. Iterate through and count transitions.
Hint 2: For each position where A[i] ≠ A[i-1], increment the count. Don't forget to count the first element.
Hint 3: Edge case: empty array has 0 distinct elements.
public int Distinct(int[] A) { if (A.Length == 0) return 0; int count = 1; // At least one element for (int i = 1; i < A.Length; i++) { if (A[i] != A[i - 1]) { count++; } } return count; } // Trace Example 1: A = [1, 1, 2, 2, 3, 3] // i=1: A[1]=1 == A[0]=1? yes, skip // i=2: A[2]=2 != A[1]=1? yes, count=2 // i=3: A[3]=2 == A[2]=2? yes, skip // i=4: A[4]=3 != A[3]=2? yes, count=3 // i=5: A[5]=3 == A[4]=3? yes, skip // Return 3 ✓
N fish are in a stream. Each fish has a size A[i] and direction B[i] (0 = upstream, 1 = downstream).
When two fish meet: the larger fish eats the smaller. A downstream fish and an upstream fish will meet if downstream is to the left. Surviving fish never go back to meet previously passed fish.
Count how many fish survive to the end.
Hint 1: Key insight: a fish survives if it's not eaten by any fish that comes after it (going in the same or opposite direction).
Hint 2: Use a stack: process fish left to right. Downstream fish can be eaten by upstream fish that come later. Keep track of active downstream fish.
Hint 3: When we encounter an upstream fish, pop all downstream fish from stack that are smaller. If stack becomes empty, the upstream fish survives.
public int Fish(int[] A, int[] B) { // B[i] = 1 means downstream (→), B[i] = 0 means upstream (←) // Downstream fish are added to a stack. // When an upstream fish comes, it eats downstream fish if larger. Stack<int> downstreamStack = new Stack<int>(); int survivors = 0; for (int i = 0; i < A.Length; i++) { int size = A[i]; int direction = B[i]; bool alive = true; if (direction == 1) { // Downstream fish: add to stack (potential meals) downstreamStack.Push(size); } else { // Upstream fish: may eat downstream fish from stack while (downstreamStack.Count > 0 && alive) { int topDownstream = downstreamStack.Pop(); if (size > topDownstream) { // Upstream fish eats downstream fish // Continue fighting } else { // Downstream fish eats upstream fish downstreamStack.Push(topDownstream); alive = false; } } } if (alive) { survivors++; } } // All remaining fish in stack survive (no more upstream fish) survivors += downstreamStack.Count; return survivors; } // Trace Example: A = [4, 3, 2, 1, 5], B = [0, 1, 0, 0, 0] // i=0: size=4, dir=0 (upstream), stack empty, alive=true, survivors=1 // i=1: size=3, dir=1 (downstream), push 3, stack={3} // i=2: size=2, dir=0 (upstream), pop 3, 2 < 3? yes, push 3 back, alive=false // i=3: size=1, dir=0 (upstream), pop 3, 1 < 3? yes, push 3 back, alive=false // i=4: size=5, dir=0 (upstream), pop 3, 5 > 3? yes, stack empty, alive=true // survivors=2 // Final: survivors + stack.Count = 2 + 0 = 2 // Fish: 4 (upstream) and 5 (upstream) survive ✓
Given array A with both positive and negative integers, you can choose a subset S ⊆ A.
Find the minimum possible absolute value of the sum of elements in S. (Empty subset is allowed.)
Hint 1: This is a variant of the subset sum problem. With N ≤ 20, we can enumerate all 2^N subsets.
Hint 2: For each subset (using bitmask 0 to 2^N - 1), compute its sum and track the minimum absolute value.
Hint 3: Optimization: separate positive and negative elements. Use DP: dp[i] = can we achieve sum i? Then find the sum with minimum |sum|.
public int MinAbsSum(int[] A) { int N = A.Length; long minAbs = long.MaxValue; // Enumerate all 2^N subsets using bitmask for (int mask = 0; mask < (1 << N); mask++) { long sum = 0; for (int i = 0; i < N; i++) { if ((mask & (1 << i)) != 0) { sum += A[i]; } } minAbs = Math.Min(minAbs, Math.Abs(sum)); } return (int)minAbs; } // Trace Example: A = [1, 5, 2, -6] // mask=0 ({}): sum=0, |0|=0 → minAbs=0 // (No need to check further; we found 0) // Return 0 ✓
For larger constraints, use DP to find all achievable sums efficiently:
public int MinAbsSumDP(int[] A) { // Separate positive and negative long posSum = 0, negSum = 0; foreach (int x in A) { if (x > 0) posSum += x; else negSum += -x; } // DP: achievable[sum] = true if we can make that sum long target = posSum + negSum; bool[] achievable = new bool[target + 1]; achievable[0] = true; // Build DP for positive numbers foreach (int x in A) { if (x > 0) { for (long s = target; s >= x; s--) { achievable[s] = achievable[s] || achievable[s - x]; } } } // Find minimum |sum| where sum is achievable and has // a positive contribution from negatives balanced with positives long minAbs = target; for (long pos = 0; pos <= target; pos++) { if (achievable[pos]) { long neg = target - pos; minAbs = Math.Min(minAbs, Math.Abs(neg - pos)); } } return (int)minAbs; }
Understand how your performance maps to interview readiness.
| Score Range | Status | Interpretation |
|---|---|---|
| 250–300 | Excellent | You're ready. All problems solved or very close. You should pass the real round. |
| 210–249 | Good | Strong performance. Easy + Medium complete, Hard partially done. Likely to pass. |
| 150–209 | Fair | Mixed results. Easy complete, Medium partially. Study the medium-difficulty patterns more. |
| 100–149 | Needs Work | Only Easy problem solved per test. Review algorithms and practice more before attempting real round. |
| <100 | Insufficient | Not ready. Return to fundamentals. Study arrays, hashing, basic DP before retrying. |