CodeSignal Practice Test: 8 GCA-Style Questions with Solutions
These are 8 original practice questions shaped like the CodeSignal General Coding Assessment (GCA): two rounds of 4 questions that get harder, each round meant to be done in 70 minutes. In each round, questions 1 and 2 are quick implementation tasks, question 3 is a longer simulation, and question 4 needs an efficient data structure to pass large hidden tests. Every question has examples, an approach, a tested Python 3 solution and the edge cases hidden tests would check. None of them are real GCA questions, so pair them with CodeSignal's free practice assessments in the real editor.
A fitness tracker logs your heart rate once a minute in the array readings. A reading is a turning point if it is strictly higher than both of its neighbors or strictly lower than both. The first and last readings have only one neighbor, so they never count. Return the number of turning points.
Examples
Input
readings = [70, 75, 72, 72, 80, 78]
Output
2
Why
75 and 80 are peaks. The two 72s do not count: a reading equal to a neighbor is not strictly higher or lower.
Input
readings = [60, 61]
Output
0
Constraints
1 <= len(readings) <= 1,000
30 <= readings[i] <= 220
Show approach and solution
Approach
Walk the inner positions 1 to n - 2 and compare each reading with its two neighbors. Count it when it is above both or below both. Arrays shorter than 3 have no inner positions, so the loop never runs and the answer is 0.
Python 3 solution
def solution(readings):
count = 0
for i in range(1, len(readings) - 1):
left, mid, right = readings[i - 1], readings[i], readings[i + 1]
if (mid > left and mid > right) or (mid < left and mid < right):
count += 1
return count
Complexity: O(n) time, O(1) extra space.
What the hidden tests check: Arrays of length 1 and 2, flat stretches of equal values, strictly increasing arrays (answer 0), and zigzag arrays where every inner reading counts.
Problem 2EasyRound 1Greedy simulation
Cut a playlist into radio blocks
def solution(lengths: list[int], limit: int) -> int
You are cutting a playlist into radio blocks. Songs stay in order. Add songs to the current block one at a time; if the next song would push the block's total past limit seconds, close the block and start a new one with that song. A song longer than limit gets a block to itself. Return the number of blocks.
Examples
Input
lengths = [200, 180, 240, 90, 300], limit = 450
Output
3
Why
Blocks are [200, 180], [240, 90] and [300]. Adding 240 to the first block would make 620, which is past 450.
Input
lengths = [500, 100], limit = 300
Output
2
Why
The 500-second song is longer than the limit, so it sits alone.
Constraints
0 <= len(lengths) <= 10,000
1 <= lengths[i] <= 3,600
1 <= limit <= 100,000
Show approach and solution
Approach
Keep the running total of the open block. For each song, start a new block if there is no block yet or the song would push the total past the limit; otherwise add it. A block that lands exactly on the limit is allowed, because "past" means greater than.
Python 3 solution
def solution(lengths, limit):
blocks = 0
current = 0
for song in lengths:
if blocks == 0 or current + song > limit:
blocks += 1
current = song
else:
current += song
return blocks
Complexity: O(n) time, O(1) extra space.
What the hidden tests check: An empty playlist (0 blocks), a block that ends exactly at the limit, songs longer than the limit, and a single song.
A puzzle board is a grid of non-negative integers where 0 is an empty cell. When the player swipes left, every row changes the same way: tiles slide left past empty cells, and two equal tiles that end up next to each other merge into one tile with double the value. A tile made by a merge cannot merge again in the same swipe, and merges are resolved from left to right. Return the board after one swipe left.
In row 1 the two 2s merge into a 4, and that new 4 does not merge with the existing 4. In row 3 the leftmost pair merges first, so the third 2 stays on its own.
Input
board = [[0, 0], [3, 3]]
Output
[[0, 0], [6, 0]]
Constraints
1 <= rows, cols <= 100
0 <= board[i][j] <= 2^20
Show approach and solution
Approach
Handle each row on its own. Drop the zeros, then walk the remaining tiles left to right: if a tile equals the next one, write their sum and skip both; otherwise write the tile and move one step. Skipping both tiles is what stops a merged tile from merging again. Pad the row with zeros back to its width.
Python 3 solution
def solution(board):
result = []
for row in board:
tiles = [value for value in row if value != 0]
merged = []
i = 0
while i < len(tiles):
if i + 1 < len(tiles) and tiles[i] == tiles[i + 1]:
merged.append(tiles[i] * 2)
i += 2
else:
merged.append(tiles[i])
i += 1
merged += [0] * (len(row) - len(merged))
result.append(merged)
return result
Complexity: O(rows x cols) time, O(cols) extra space per row.
What the hidden tests check: Rows of all zeros, rows with no merges, four equal tiles in a row (two merges, not one), a single column, and zeros between two equal tiles.
Problem 4HardRound 1Prefix sums + hash map
Balanced windows in a sign-in log
def solution(log: str) -> int
A server log is a string of 'I' (a user signs in) and 'O' (a user signs out). A window is any contiguous substring of the log. Return the number of windows that contain the same number of 'I' and 'O' characters.
Examples
Input
log = "IOIO"
Output
4
Why
The balanced windows are "IO" (twice), "OI" and "IOIO".
Input
log = "IIO"
Output
1
Input
log = "III"
Output
0
Constraints
1 <= len(log) <= 100,000
Checking every window is O(n^2), which is too slow for the largest tests.
Show approach and solution
Approach
Keep a running balance: +1 for I and -1 for O. A window is balanced exactly when the balance at its end equals the balance just before its start. So count, for every position, how many earlier positions had the same balance, using a hash map from balance to count. Seed the map with balance 0 seen once, for windows that start at the beginning.
Python 3 solution
def solution(log):
seen = {0: 1}
balance = 0
windows = 0
for event in log:
balance += 1 if event == 'I' else -1
windows += seen.get(balance, 0)
seen[balance] = seen.get(balance, 0) + 1
return windows
Complexity: O(n) time, O(n) space for the map.
What the hidden tests check: Long strings of one letter (answer 0), perfectly alternating strings (the answer grows to about n^2 / 4), and 100,000 characters to rule out the O(n^2) check.
Problem 5EasyRound 2String checks
Password rules
def solution(passwords: list[str]) -> int
A sign-up form accepts a password only if all four rules hold: it has at least 8 characters, at least one digit, at least one uppercase letter, and no character appears three or more times in a row. Given a list of passwords, return how many are accepted.
"password1" has no uppercase letter, "AAAbbb123" has three A's in a row and "Ok1" is too short.
Input
passwords = ["Xx11Yy22", "Xx111Yy2"]
Output
1
Constraints
1 <= len(passwords) <= 1,000
Each password has 1 to 100 printable ASCII characters and no spaces.
Show approach and solution
Approach
Write one helper that checks a single password and returns early on the first broken rule, then count the passwords that pass. For the run rule, compare each character with the two before it.
Python 3 solution
def is_valid(password):
if len(password) < 8:
return False
if not any(ch.isdigit() for ch in password):
return False
if not any(ch.isupper() for ch in password):
return False
for i in range(2, len(password)):
if password[i] == password[i - 1] == password[i - 2]:
return False
return True
def solution(passwords):
return sum(1 for password in passwords if is_valid(password))
Complexity: O(total characters) time, O(1) extra space.
What the hidden tests check: Passwords of exactly 8 characters, a triple at the very end, symbols that are neither letters nor digits, and passwords that break only one rule.
Problem 6EasyRound 2String manipulation
Shift each word by its position
def solution(message: str) -> str
Encode a message by shifting every letter forward in the alphabet by the index of the word it belongs to: letters in the first word (index 0) stay the same, letters in the second word move 1 place, the third word 2 places, and so on. Shifts wrap from z back to a, uppercase letters stay uppercase, and anything that is not a letter stays as it is. Words are separated by single spaces.
Examples
Input
message = "meet at noon"
Output
"meet bu pqqp"
Input
message = "Zoo Zebra!"
Output
"Zoo Afcsb!"
Why
The second word shifts by 1, so Z wraps to A, and the ! is left alone.
Constraints
0 <= len(message) <= 10,000
There can be thousands of words, so shifts go far past 26.
Show approach and solution
Approach
Split on single spaces, shift each letter by the word index modulo 26 using its offset from 'a' or 'A', and join the words back with single spaces. Taking the shift modulo 26 handles word 26 and later.
Python 3 solution
def shift_letter(ch, k):
if 'a' <= ch <= 'z':
return chr((ord(ch) - ord('a') + k) % 26 + ord('a'))
if 'A' <= ch <= 'Z':
return chr((ord(ch) - ord('A') + k) % 26 + ord('A'))
return ch
def solution(message):
words = message.split(' ')
encoded = []
for index, word in enumerate(words):
encoded.append(''.join(shift_letter(ch, index) for ch in word))
return ' '.join(encoded)
Complexity: O(n) time and space for the new string.
What the hidden tests check: Word 26 and later (the shift wraps back to no change), digits and punctuation inside words, uppercase Z, and the empty string.
A garage has n spots numbered 1 to n. You get a list of events in time order. "arrive <plate>" parks that car in the lowest-numbered free spot; "leave <plate>" frees the car's spot. Return a list with one entry per arrival: the spot the car got, or -1 if the garage was full (that car drives away). A leave event for a car that is not parked does nothing.
Scanning for the lowest free spot on every arrival is O(n) each time, too slow at 100,000 events. Keep the free spots in a min-heap so the lowest one pops in O(log n), and a dict from plate to spot so a leave can push the spot back. A car that was turned away is never in the dict, so its leave event is ignored.
Python 3 solution
import heapq
def solution(n, events):
free = list(range(1, n + 1))
heapq.heapify(free)
parked = {}
assigned = []
for event in events:
action, plate = event.split()
if action == 'arrive':
if free:
spot = heapq.heappop(free)
parked[plate] = spot
assigned.append(spot)
else:
assigned.append(-1)
elif plate in parked:
heapq.heappush(free, parked.pop(plate))
return assigned
Complexity: O((n + m) log n) time for m events, O(n) space.
What the hidden tests check: A full garage, leave events for cars that were turned away, spots freed out of order (the next car must still get the lowest number), and 100,000 events.
Problem 8HardRound 2Sliding window
Shortest stretch with every category
def solution(items: list[str]) -> int
A store's checkout log lists product categories in the order they were scanned during the day. Find the shortest contiguous stretch of the log that includes every category that appears anywhere in the log at least once. Return its length.
There are 3 categories, and ["eggs", "milk", "bread"] holds all of them.
Input
items = ["tea", "tea", "tea"]
Output
1
Constraints
1 <= len(items) <= 100,000
Checking every stretch is O(n^2) or worse, too slow for the largest tests.
Show approach and solution
Approach
Count the distinct categories first. Then grow a window to the right, keeping a count per category and how many categories are covered. Whenever the window covers them all, record its length and shrink it from the left until it no longer does. Each index enters and leaves the window once.
Python 3 solution
from collections import defaultdict
def solution(items):
needed = len(set(items))
counts = defaultdict(int)
covered = 0
best = len(items)
left = 0
for right, item in enumerate(items):
counts[item] += 1
if counts[item] == 1:
covered += 1
while covered == needed:
best = min(best, right - left + 1)
counts[items[left]] -= 1
if counts[items[left]] == 0:
covered -= 1
left += 1
return best
Complexity: O(n) time, O(k) space for k categories.
What the hidden tests check: A single category, every item different (the answer is the whole log), the shortest stretch at the very end, and 100,000 items so the O(n^2) scan is too slow.
How to practice for the CodeSignal round
Step 1
Do one round per sitting
Set a 70-minute timer and open all 4 questions of a round at once, like the real test. No AI, and look up syntax only, which is what the GCA allows.
Step 2
Submit a working version first
Get each question passing its examples before you polish it. On the GCA your best submission counts and partial credit comes from tests passed, so a working first version is never wasted.
Step 3
Grade yourself with the hidden-test notes
When the timer ends, run your code on the edge cases listed under each solution. Write down the ones you missed; they are the habits to fix.
Step 4
Finish in the real editor
Take CodeSignal's free practice assessment so the editor, the sample tests and the submit flow feel familiar on test day. Companies cannot see your practice activity.
OphyAI Desktop for Mac and Windows
Practice with these, then use Coding Copilot in your CodeSignal interview
Coding Copilot in the OphyAI desktop app reads the coding problem on your screen with one hotkey and returns an approach, complexity notes and working code in your language.
Practice with these, then bring Coding Copilot into the real CodeSignal interview. While you practice, press Cmd/Ctrl + Shift + A to snap the problem window, or Cmd/Ctrl + Shift + S to drag a box around one question, and Coding Copilot returns an answer card: how it read the problem, what to say first, the code with line numbers, a dry run with edge cases, and the Big O. Compare it with your own attempt.
In a live CodeSignal interview, press Space or Cmd/Ctrl + Shift + L and it transcribes the question your interviewer asks out loud. The card shows in a floating overlay that macOS and Windows leave out of screen sharing and recording. It stays on top of a full-screen editor or browser and opens without taking focus from your editor.
Cmd/Ctrl + Shift + A snaps the problem window, even when other windows cover it, and Cmd/Ctrl + Shift + S lets you drag a box around part of the screen. In a live interview, Space or Cmd/Ctrl + Shift + L transcribes the question the interviewer asks out loud.
Answers show as a card in a floating overlay that macOS and Windows leave out of screen sharing and recording. It stays on top of a full-screen editor or browser and opens without taking the keyboard from your editor.
Each answer card shows how OphyAI read the problem, what to say first, the code with line numbers, a dry run with edge cases, and the Big O. Follow-ups remember the problem, so "now handle duplicates" updates the same code.
Free download. Coding Interview is on the Premium plan ($59 a month, 500 credits) and uses 15 credits per session. See everything it does
What it is hidden from, and what it is not
On the proctored GCA, CodeSignal records your camera, microphone and screen. Companies can also turn on CodeSignal's own AI assistant for an assessment.
The OphyAI window is hidden from screen sharing and recording, but not from a webcam, a person watching, or the platform's own keystroke, paste and tab tracking.
CodeSignal practice test FAQ
Start with CodeSignal's free practice assessments, which use the real editor; companies cannot see your activity there. Then use the 8 original questions on this page for full timed runs of 4 questions in 70 minutes.
No. CodeSignal's practice area shows the editor and the question types, and this page copies the GCA's shape of 4 questions that get harder, but the real test draws from a very large pool. CodeSignal says the GCA has over 628,000 variations, so practice the patterns, not specific questions.
The one you write fastest without looking things up. The GCA lists 38 languages, including Python 3, Java, C++, JavaScript and Go. The solutions here are in Python 3 because it keeps the code short.
There is no official passing score; each company sets its own. Points come from the test cases your submissions pass, so a partial solution still scores, and bonus points come only from fully solving a group of questions.
Our plan: read all 4 first, then about 15 minutes for questions 1 and 2 together, about 25 for question 3, and the rest for question 4. Submit something on every question, because your best submission counts.
The proctored GCA records your camera, microphone and screen for the whole test. Our CodeSignal assessment guide covers proctoring and scoring in full.
Yes. Coding Copilot in the OphyAI desktop app reads a problem on your screen with Cmd/Ctrl + Shift + A and returns an approach, complexity notes and working code, so you can check your attempts at these questions. It also works on the CodeSignal test itself and in live CodeSignal interviews, in an overlay that screen sharing and recording leave out. It does not hide from your webcam or from CodeSignal's own proctoring.
Sources
Format facts read on November 3, 2026. Every problem on this page is original and written by OphyAI for practice; none is a real CodeSignal question. OphyAI is not affiliated with CodeSignal.
Practice here, then take Coding Copilot into your CodeSignal interview
Download OphyAI Desktop and bring Coding Copilot into your next coding interview: it hears the question, reads the problem, and shows the approach and code in an overlay your screen share leaves out.