PracHub
QuestionsCoachesLearningGuidesInterview Prep
|Home/Coding & Algorithms/Capital One

Solve four algorithmic coding tasks

Last updated: Jun 24, 2026

Quick Overview

This multi-part question evaluates algorithmic problem-solving across frequency counting (equal-value pairs), simulation and in-place grid transformation (match-three gravity), matrix diagonal pattern checking, and interval/range data-structure management, emphasizing correctness under constraints and rigorous time/space complexity reasoning.

  • Medium
  • Capital One
  • Coding & Algorithms
  • Software Engineer

Solve four algorithmic coding tasks

Company: Capital One

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: Medium

Interview Round: Take-home Project

You will solve four independent coding tasks: 1) Count equal-value pairs in an array: Given an integer array nums of length n, return the number of index pairs (i, j) with 0 ≤ i < j < n and nums[i] = nums[j]. Target time/space: O(n) time using O(n) auxiliary space. Explain the approach and complexity. 2) Simulate a match-three board with gravity: Given an R × C grid board of uppercase letters (colors) or '.' for empty. Repeatedly, in a single round, mark every cell that belongs to any horizontal or vertical run of length ≥ 3 of the same letter (runs can overlap). Remove all marked cells (set to '.'), then apply gravity so that in each column letters fall down and '.' move up. Repeat rounds until the board is stable (no removals). Return the final grid. Constraints: 1 ≤ R, C ≤ 50. Describe the algorithm and its complexity. 3) Check an X-pattern in a square matrix: Given an n × n integer matrix, return true iff every element on the main diagonal and the anti-diagonal is non-zero and every other element is zero; otherwise return false. Constraints: 1 ≤ n ≤ 100. Provide the check and complexity. 4) Process block placement queries on a line: You have N empty positions labeled 1..N. Process Q operations of two types: - Place l r: mark all positions in the inclusive interval [l, r] as occupied (placing on already-occupied cells has no additional effect). - Query k: return the leftmost starting index s such that there exists a contiguous unoccupied segment [s, s+k-1] of length k; if no such segment exists, return -1. Design a data structure to support up to N, Q ≤ 2×10^5 with near O((N + Q) log N) total time. Explain the data structures used and provide the algorithm.

Quick Answer: This multi-part question evaluates algorithmic problem-solving across frequency counting (equal-value pairs), simulation and in-place grid transformation (match-three gravity), matrix diagonal pattern checking, and interval/range data-structure management, emphasizing correctness under constraints and rigorous time/space complexity reasoning.

Solution

## Overview Four independent tasks, scored separately. Below, each part gives the approach, reference implementation (Python), correctness reasoning, edge cases, and complexity. The difficulty ramp is real: Parts 1 and 3 are linear scans, Part 2 is a fixed-point grid simulation, and Part 4 needs a segment tree with lazy range-assignment and a guided descent. --- ## Part 1 — Count equal-value pairs ### Approach Brute force over all pairs is $O(n^2)$. Instead, group indices by value: a value occurring $c$ times contributes $\binom{c}{2} = \frac{c(c-1)}{2}$ pairs $(i, j)$ with $i < j$. We can accumulate this in a single pass using the identity $$\binom{c}{2} = \sum_{t=0}^{c-1} t,$$ i.e. the $t$-th occurrence of a value forms exactly $t$ new pairs with its $t$ earlier occurrences. So we keep a running frequency map and, when we see a value for the $(t+1)$-th time, add the current count `t` to the answer *before* incrementing. ### Implementation ```python from collections import defaultdict def count_equal_pairs(nums): seen = defaultdict(int) ans = 0 for x in nums: ans += seen[x] # pairs with all earlier equal elements seen[x] += 1 return ans ``` Equivalently, count frequencies first and sum $c(c-1)//2$ over values — both are $O(n)$. ### Correctness Each unordered pair $(i, j)$, $i < j$, with `nums[i] == nums[j]` is counted exactly once: at the moment we process index $j$, `seen[nums[j]]` equals the number of earlier equal elements, all with index $< j$. Summed over $j$, this is the total pair count. ### Edge cases & pitfalls - **Overflow**: with $n$ identical values the answer is $\binom{n}{2} \approx n^2/2$. For $n = 2\times10^5$ that is $\sim 2\times10^{10}$, which exceeds 32-bit range — use a 64-bit integer (Python's `int` is arbitrary precision; in Java/C++ use `long`). - Empty array or all-distinct array → `0`. ### Complexity $O(n)$ time, $O(n)$ auxiliary space (the frequency map). Matches the target. --- ## Part 2 — Match-three board with gravity (LeetCode 723 "Candy Crush") ### Approach One **round** has three phases, and the key correctness point is that marking must be computed against the *current, un-mutated* board: 1. **Mark**: scan all rows for horizontal runs of $\ge 3$ equal letters and all columns for vertical runs of $\ge 3$; flag every cell in any such run. Overlapping runs are naturally handled because we OR the flags. Use a separate boolean array (or a reversible in-place marker) so a cell already flagged horizontally still gets checked vertically against its original letter. 2. **Remove**: set every flagged cell to `'.'`. 3. **Gravity**: for each column independently, let the surviving letters fall to the bottom and push `'.'` to the top. Repeat until a round flags nothing (a fixed point). ### Implementation ```python def candy_crush(board): if not board or not board[0]: return board R, C = len(board), len(board[0]) while True: to_clear = [[False] * C for _ in range(R)] found = False # Horizontal runs (>= 3) for r in range(R): run = 1 for c in range(1, C): if board[r][c] != '.' and board[r][c] == board[r][c - 1]: run += 1 else: if run >= 3: for k in range(c - run, c): to_clear[r][k] = True found = True run = 1 if run >= 3: for k in range(C - run, C): to_clear[r][k] = True found = True # Vertical runs (>= 3) for c in range(C): run = 1 for r in range(1, R): if board[r][c] != '.' and board[r][c] == board[r - 1][c]: run += 1 else: if run >= 3: for k in range(r - run, r): to_clear[k][c] = True found = True run = 1 if run >= 3: for k in range(R - run, R): to_clear[k][c] = True found = True if not found: return board # Remove for r in range(R): for c in range(C): if to_clear[r][c]: board[r][c] = '.' # Gravity per column (letters fall to bottom) for c in range(C): write = R - 1 for r in range(R - 1, -1, -1): if board[r][c] != '.': board[write][c] = board[r][c] write -= 1 for r in range(write, -1, -1): board[r][c] = '.' ``` ### Correctness - **Simultaneous marking**: computing `to_clear` fully from the pre-removal board, then deleting, guarantees overlapping vertical/horizontal runs (e.g. a cell at the intersection of a row-run and a column-run) are all cleared in the same round, matching the problem's "mark every cell in any run" semantics. - The `board[r][c] != '.'` guard prevents an empty cell from extending a run. - **Gravity** writes survivors bottom-up so their relative vertical order is preserved, then fills the top with `'.'`. - The `while` loop terminates because every productive round strictly reduces the count of non-`'.'` cells, which is bounded by $R \cdot C$. ### Edge cases - A run exactly at the right/bottom edge is handled by the post-loop `if run >= 3` check. - No runs anywhere → returns immediately, board unchanged. - A board that is a single row or single column works since horizontal and vertical scans are independent. ### Complexity Each round is $O(RC)$ (constant number of full passes). The number of rounds is $O(RC)$ in the worst case (each round may clear as few as 3 cells), so worst case $O(R^2C^2)$. With $R, C \le 50$ that is at most $\sim 6\times10^6$ cell-touches — comfortably fast. Space is $O(RC)$ for the marker array. --- ## Part 3 — X-pattern check in a square matrix (LeetCode 2319) ### Approach A cell $(i, j)$ in an $n \times n$ matrix lies on the **main diagonal** iff $i = j$ and on the **anti-diagonal** iff $i + j = n - 1$. The pattern is valid iff: - every on-diagonal cell (on either diagonal) is **non-zero**, and - every off-diagonal cell is **zero**. One $O(n^2)$ pass classifies each cell and verifies the right condition. ### Implementation ```python def check_x_matrix(grid): n = len(grid) for i in range(n): for j in range(n): on_diag = (i == j) or (i + j == n - 1) if on_diag: if grid[i][j] == 0: return False else: if grid[i][j] != 0: return False return True ``` ### Correctness The two predicates $i = j$ and $i + j = n - 1$ are exactly the membership tests for the two diagonals; their union is the "X". The loop enforces the on/off condition for every cell, so it returns `True` only when the whole matrix matches. ### Edge cases - **$n = 1$**: the single cell $(0,0)$ satisfies both $i = j$ and $i + j = n - 1 = 0$, so it is on-diagonal and must be non-zero — the code returns `True` iff `grid[0][0] != 0`, which is the intended result. - **Odd $n$**: the center cell $(m, m)$ where $m = (n-1)/2$ lies on both diagonals; it is correctly treated as on-diagonal (must be non-zero), and we never require it to be zero. ### Complexity $O(n^2)$ time, $O(1)$ extra space. --- ## Part 4 — Block-placement queries (segment tree on free runs, LeetCode 3161 "Block Placement Queries") ### Goal Support, over positions $1 \ldots N$: - `Place l r`: set $[l, r]$ occupied (idempotent). - `Query k`: leftmost $s$ with $[s, s+k-1]$ fully free, else $-1$. with $N, Q \le 2\times10^5$ at $O((N+Q)\log N)$. ### Data structure A **segment tree** over positions where each node covering range $[lo, hi]$ stores, for the cells currently free in that range: - `best` — length of the longest run of consecutive free cells inside $[lo, hi]$, - `pref` — length of the longest free run starting at `lo`, - `suf` — length of the longest free run ending at `hi`, - a lazy `assign` flag for range "set occupied" (we only ever set occupied, never free, so a single lazy value `OCCUPIED` suffices). A leaf for a free cell has `best = pref = suf = 1`; for an occupied cell all are `0`. ### Merge rule Combining left child $L$ (length $\ell$) and right child $R$ (length $r$): ``` node.best = max(L.best, R.best, L.suf + R.pref) # run straddling the boundary node.pref = L.pref + (R.pref if L.pref == ℓ else 0) # left fully free spills into right node.suf = R.suf + (L.suf if R.suf == r else 0) ``` The crucial middle term `L.suf + R.pref` stitches free cells across the child boundary, which is what lets us find runs that span subtrees. ### Place (range-assign occupied with lazy propagation) Standard lazy segment-tree range update: if a node's range is fully inside $[l, r]$, set all three stats to `0` and mark its lazy flag; otherwise push down the pending flag and recurse. Each `Place` is $O(\log N)$. ### Query (leftmost start of a free run of length $\ge k$) If the root's `best < k`, return $-1$. Otherwise descend, always preferring the **leftmost** valid placement: 1. If the **left** child contains a free run of length $\ge k$ (`left.best >= k`), recurse into the left child. 2. Else if the run can **straddle** the boundary — `left.suf + right.pref >= k` — the leftmost start sits inside the left child's suffix: $s = (\text{mid} - \text{left.suf} + 1)$ (1-indexed), and we return it directly. 3. Else recurse into the **right** child. Because we check left and the straddle before right, the first valid start found is the leftmost. Each query is $O(\log N)$. ### Implementation (sketch) ```python import sys class Node: __slots__ = ('best', 'pref', 'suf', 'assign') def __init__(self): self.best = self.pref = self.suf = 0 self.assign = False # pending "set occupied" class SegTree: def __init__(self, n): self.n = n self.t = [Node() for _ in range(4 * n)] self._build(1, 1, n) def _build(self, node, lo, hi): nd = self.t[node] if lo == hi: nd.best = nd.pref = nd.suf = 1 # all cells start free return mid = (lo + hi) // 2 self._build(2 * node, lo, mid) self._build(2 * node + 1, mid + 1, hi) self._pull(node, lo, hi) def _pull(self, node, lo, hi): mid = (lo + hi) // 2 L, R, nd = self.t[2 * node], self.t[2 * node + 1], self.t[node] llen, rlen = mid - lo + 1, hi - mid nd.best = max(L.best, R.best, L.suf + R.pref) nd.pref = L.pref + (R.pref if L.pref == llen else 0) nd.suf = R.suf + (L.suf if R.suf == rlen else 0) def _apply_occupied(self, node): nd = self.t[node] nd.best = nd.pref = nd.suf = 0 nd.assign = True def _push(self, node): if self.t[node].assign: self._apply_occupied(2 * node) self._apply_occupied(2 * node + 1) self.t[node].assign = False def place(self, node, lo, hi, l, r): if r < lo or hi < l: return if l <= lo and hi <= r: self._apply_occupied(node) return self._push(node) mid = (lo + hi) // 2 self.place(2 * node, lo, mid, l, r) self.place(2 * node + 1, mid + 1, hi, l, r) self._pull(node, lo, hi) def query(self, k): if self.t[1].best < k: return -1 node, lo, hi = 1, 1, self.n while lo != hi: self._push(node) mid = (lo + hi) // 2 L, R = self.t[2 * node], self.t[2 * node + 1] if L.best >= k: # leftmost run fits entirely in left node, hi = 2 * node, mid elif L.suf + R.pref >= k: # run straddles the boundary return mid - L.suf + 1 else: # must be in the right child node, lo = 2 * node + 1, mid + 1 return lo # single free cell, k == 1 ``` Driver: build `SegTree(N)`, then for each op call `st.place(1, 1, N, l, r)` or print `st.query(k)`. ### Correctness - The merge rule maintains the invariant that `best/pref/suf` exactly describe the free runs of each node's range; the straddle term is what makes cross-boundary runs visible. - `Place` only ever marks cells occupied, so a single lazy `assign` (no "set free") is sound and idempotent — re-placing an occupied range just re-zeros already-zero stats. - The descent returns the leftmost start because at every node it exhausts left-then-straddle before going right; the straddle start $\text{mid} - L.\text{suf} + 1$ is the earliest position whose suffix run reaches length $k$. ### Edge cases - `k > N` or no free run of length $k$ → root `best < k` → returns $-1$. - `Place` with $l > r$ shouldn't occur given well-formed input; if it can, guard it. - `k = 1` resolves at a leaf and returns that position. ### Complexity Build $O(N)$, each `Place` and `Query` $O(\log N)$, so total $O((N + Q)\log N)$ time and $O(N)$ space — meeting the target. --- ## Summary table | Part | Technique | Time | Space | |------|-----------|------|-------| | 1 | Hash-map frequency + $\binom{c}{2}$ | $O(n)$ | $O(n)$ | | 2 | Mark-all → remove → per-column gravity, looped | $O(R^2C^2)$ worst (tiny for $R,C\le50$) | $O(RC)$ | | 3 | Single $O(n^2)$ diagonal-membership scan | $O(n^2)$ | $O(1)$ | | 4 | Segment tree (best/pref/suf) + lazy range-assign + guided descent | $O((N+Q)\log N)$ | $O(N)$ | ### Notes on the follow-ups - **Part 1 windowed ($j - i \le D$)**: slide a window of size $D$; as index $j$ enters, add `count[nums[j]]` (occurrences currently in the window) to the answer, then insert; when an index leaves the left edge, decrement its count. $O(n)$ with a hash map keyed by value. - **Part 2 with diagonals**: add two more marking passes along the two diagonal directions ($i+j$ constant and $i-j$ constant); the mark-then-remove discipline and the $O(R^2C^2)$ bound are unchanged. - **Part 4 with `Erase l r`**: add a second lazy state ("set free") alongside "set occupied"; lazy values must be ordered/overwritten correctly (a newer assignment replaces an older pending one of either kind), and leaves reset to `best=pref=suf=1`. Each op stays $O(\log N)$.

Related Interview Questions

  • Solve Four Coding Assessment Tasks - Capital One (medium)
  • Write SQL using joins and window functions - Capital One (medium)
  • Review Preprocessing Code and Tests - Capital One (easy)
  • Remove nodes with a given value - Capital One (medium)
  • Solve multiple algorithmic interview questions - Capital One (hard)
|Home/Coding & Algorithms/Capital One

Solve four algorithmic coding tasks

Capital One logo
Capital One
Sep 6, 2025, 12:00 AM
MediumSoftware EngineerTake-home ProjectCoding & Algorithms
37
0

You are given four independent coding tasks. They may be solved in any order, and partial test-case credit is awarded for each task. The tasks are roughly increasing in difficulty: the first is a warm-up array problem and the last requires a non-trivial data structure.

For each task, implement a correct, efficient solution and clearly state its time and space complexity in terms of the stated input sizes.

Constraints & Assumptions

  • The four tasks are scored independently; a partially-passing solution to a later task can still earn points, so attempt every task.
  • Inputs are well-formed (valid array, rectangular grid, square matrix, in-range query parameters) unless a task says otherwise — but you should still articulate any assumptions you make.
  • Per-task size limits are given inside each Part; respect them when choosing an asymptotically efficient approach.

Clarifying Questions to Ask

  • What are the value ranges of the array / matrix entries (do they fit in a 32-bit or 64-bit integer, and can the equal-pair count overflow)?
  • For the match-three board, are diagonal runs considered, or only horizontal and vertical runs?
  • For the match-three board, in a single round are all marks computed from the pre-removal board simultaneously, or sequentially as cells are removed?
  • For the block-placement task, are the Place and Query operations interleaved arbitrarily, and is k always in [1,N][1, N][1,N] ?
  • What is the expected output format (return value, printed grid, boolean) for each task?

Part 1 — Count equal-value pairs in an array

Given an integer array nums of length nnn, return the number of index pairs (i,j)(i, j)(i,j) with 0≤i<j<n0 \le i < j < n0≤i<j<n and nums[i] == nums[j]. Target: O(n)O(n)O(n) time using O(n)O(n)O(n) auxiliary space.

What This Part Should Cover

  • A single-pass hash-map approach (value → running frequency) rather than the O(n2)O(n^2)O(n2) double loop.
  • Correct combinatorial counting via (c2)\binom{c}{2}(2c​) or the incremental "add prior count" technique.
  • Awareness that the answer can exceed 32-bit range when many duplicates exist (use 64-bit).

Part 2 — Simulate a match-three board with gravity

Given an R×CR \times CR×C grid board of uppercase letters (colors) or '.' for empty, simulate rounds until the board is stable:

  1. Mark every cell that belongs to any horizontal or vertical run of length ≥3\ge 3≥3 of the same letter (overlapping runs are all marked).
  2. Remove all marked cells (set them to '.' ).
  3. Gravity : in each column, existing letters fall to the bottom and '.' rise to the top.

Repeat until a round produces no removals. Return the final grid. Constraints: 1≤R,C≤501 \le R, C \le 501≤R,C≤50.

What This Part Should Cover

  • A two-phase round (mark-all-then-remove) that handles overlapping horizontal/vertical runs correctly.
  • A correct per-column gravity (compaction) step.
  • The fixed-point loop that repeats until stable, plus the worst-case complexity bound in terms of RRR , CCC .

Part 3 — Check an X-pattern in a square matrix

Given an n×nn \times nn×n integer matrix, return true iff every element on the main diagonal and the anti-diagonal is non-zero and every other element is zero; otherwise return false. Constraints: 1≤n≤1001 \le n \le 1001≤n≤100.

What This Part Should Cover

  • Correct identification of diagonal vs. off-diagonal cells via i=ji = ji=j and i+j=n−1i + j = n - 1i+j=n−1 .
  • The two complementary checks: on-diagonal cells must be non-zero, off-diagonal cells must be zero.
  • A clean single O(n2)O(n^2)O(n2) scan, plus correctness for the n=1n = 1n=1 edge case (the single cell is on both diagonals).

Part 4 — Process block-placement queries on a line

You have NNN empty positions labeled 1…N1 \ldots N1…N. Process QQQ operations of two types:

  • Place l r : mark every position in the inclusive interval [l,r][l, r][l,r] as occupied (re-placing on already-occupied cells has no extra effect).
  • Query k : return the leftmost starting index sss such that the contiguous segment [s,s+k−1][s, s+k-1][s,s+k−1] is entirely unoccupied (length kkk ); if no such segment exists, return −1-1−1 .

Design a data structure supporting N,Q≤2×105N, Q \le 2 \times 10^5N,Q≤2×105 with near O((N+Q)log⁡N)O((N + Q) \log N)O((N+Q)logN) total time. Explain the structures used and the algorithm.

What This Part Should Cover

  • A segment tree (or interval set) storing best / prefix / suffix free-run lengths per node.
  • A correct merge rule that joins free runs spanning the child boundary.
  • Lazy propagation for the range Place assignment, and a O(log⁡N)O(\log N)O(logN) descent that finds the leftmost qualifying start index.
  • Complexity argument reaching O((N+Q)log⁡N)O((N + Q)\log N)O((N+Q)logN) .

What a Strong Answer Covers

Across all four parts, a strong submission demonstrates: choosing the asymptotically right approach for each stated size limit (not just a brute force); precise complexity statements; clean handling of edge cases (empty array, n=1n = 1n=1 matrix, fully-occupied line, k>Nk > Nk>N); and idiomatic, bug-free code. The strongest candidates also note the few subtle pitfalls — integer overflow in Part 1, simultaneous-vs-sequential marking in Part 2, the both-diagonals cell in Part 3, and the boundary-straddling free run in Part 4.

Follow-up Questions

  • Part 1: How would you instead count pairs (i,j)(i, j)(i,j) where nums[i] == nums[j] and j−i≤Dj - i \le Dj−i≤D for a given window DDD ? What changes in the data structure?
  • Part 2: If diagonal runs of length ≥3\ge 3≥3 also cleared, how would your marking pass change, and would the complexity bound still hold?
  • Part 4: How would you additionally support an Erase l r operation (freeing a range) while keeping all operations O(log⁡N)O(\log N)O(logN) ? What invariant does lazy propagation need to preserve?
  • General: Several of these are independent subproblems. If they instead shared state (e.g. Part 4's line drove Part 2's gravity), how would you structure the code to keep each component testable?

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...

Browse More Questions

More Coding & Algorithms•More Capital One•More Software Engineer•Capital One Software Engineer•Capital One Coding & Algorithms•Software Engineer Coding & Algorithms
PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.