Design a data structure that accumulates points on a 2-D grid and, for any query point, counts axis-aligned squares whose one corner is that query point. The key insight: fix any diagonal corner and the other two corners are fully determined — so the answer is a product of three independent point counts.
Implement DetectSquares with two methods:
• add(point) — record a point (duplicates allowed).
• count(point) — return the number of axis-aligned squares whose one corner is the query point, counting multiplicity from duplicate adds.
Concrete example. After adding [3,10], [11,2], [3,2]:count([11,10]) → 1. The four corners (3,10)·(11,10)·(3,2)·(11,2) form a valid 8×8 square; all three non-query corners exist exactly once.count([14,8]) → 0. No square can be formed.
(px, py), choose any diagonal corner (ax, ay) where |ax − px| = |ay − py| ≠ 0. Once the diagonal is fixed, the remaining two corners are uniquely determined: (px, ay) and (ax, py). Count them independently and multiply — the product counts every distinct square built from that diagonal pair.pts: Map<"x,y", count> for fast point lookups, and xs: Map<x, Set<y>> to enumerate every distinct x-column quickly.add(x, y). Increment pts["x,y"] and insert y into xs[x]. O(1).count(px, py) — outer loop. For each distinct x-coordinate ax ≠ px, let d = ax − px. The side length is |d|.ax. For each ay at column ax, check |ay − py| = |d|. Skipping wrong-distance points is the geometry gate — it enforces the square constraint.pts[ax,ay] × pts[px,ay] × pts[ax,py] — each factor is the number of ways that corner can independently contribute, so their product is the number of valid squares using this diagonal.total and return.pts. Multiplying four counts (including the query point) double-counts in a confusing way and gives wrong answers.The outer loop runs over distinct x-values, and the inner loop over xs[ax] — together they visit every stored point exactly once. Three Map.get calls per point → O(n) per query.
(11,10).1▶class DetectSquares {2 // Map from "x,y" -> count of times that point was added3▶ private pts = new Map<string, number>();4 // Map from x-coordinate -> set of distinct y-values at that x5▶ private xs = new Map<number, Set<number>>();67 add(point: [number, number]): void {8 const [x, y] = point;9 const key = `${x},${y}`;10 this.pts.set(key, (this.pts.get(key) ?? 0) + 1);11 if (!this.xs.has(x)) this.xs.set(x, new Set());12 this.xs.get(x)!.add(y);13 }1415 count(point: [number, number]): number {16 const [px, py] = point;17 let total = 0;1819 // Iterate every candidate for the diagonal corner20 for (const [ax, ays] of this.xs) {21 if (ax === px) continue; // must be a different column22 const d = ax - px; // side length (signed)2324 for (const ay of ays) {25 if (Math.abs(ay - py) !== Math.abs(d)) continue; // must form a square2627 // The four corners are (px,py), (ax,ay), (px,ay), (ax,py)28 const c1 = this.pts.get(`${ax},${ay}`) ?? 0; // diagonal29 const c2 = this.pts.get(`${px},${ay}`) ?? 0; // same col as query30 const c3 = this.pts.get(`${ax},${py}`) ?? 0; // same row as query31 total += c1 * c2 * c3;32 }33 }34 return total;35 }36}
class DetectSquares {
// Map from "x,y" -> count of times that point was added
private pts = new Map<string, number>();
// Map from x-coordinate -> set of distinct y-values at that x
private xs = new Map<number, Set<number>>();
add(point: [number, number]): void {
const [x, y] = point;
const key = `${x},${y}`;
this.pts.set(key, (this.pts.get(key) ?? 0) + 1);
if (!this.xs.has(x)) this.xs.set(x, new Set());
this.xs.get(x)!.add(y);
}
count(point: [number, number]): number {
const [px, py] = point;
let total = 0;
// Iterate every candidate for the diagonal corner
for (const [ax, ays] of this.xs) {
if (ax === px) continue; // must be a different column
const d = ax - px; // side length (signed)
for (const ay of ays) {
if (Math.abs(ay - py) !== Math.abs(d)) continue; // must form a square
// The four corners are (px,py), (ax,ay), (px,ay), (ax,py)
const c1 = this.pts.get(`${ax},${ay}`) ?? 0; // diagonal
const c2 = this.pts.get(`${px},${ay}`) ?? 0; // same col as query
const c3 = this.pts.get(`${ax},${py}`) ?? 0; // same row as query
total += c1 * c2 * c3;
}
}
return total;
}
}pts maps a "x,y" string key to a count, enabling O(1) lookup of how many times a point was added. xs maps each x-coordinate to the set of distinct y-values at that column — this lets the count loop enumerate candidates without scanning every stored point individually.add.Increment the string-keyed count (or initialize to 1). Insert the y into the column's set. Both operations are O(1) amortized. Using a string key avoids a nested map and makes lookups in count cheap.ax === px (a zero-width square is not a square). Compute d = ax − px; the side length is |d|. We use the signed difference because the two valid diagonal y-offsets are +|d| and −|d|, and the condition |ay − py| === |d| handles both.ax, only those exactly |d| away from py can be diagonal corners. This single check enforces the square constraint without any floating-point arithmetic — everything stays integer.(ax, ay), and the two axis-aligned ones (px, ay) and (ax, py). Their counts multiply because each corner is chosen independently — it's the combinatorial product rule. total += c1 * c2 * c3.add: O(1). count: O(n) where n = distinct stored points — the double loop visits every point exactly once. Space: O(n) for both maps."x,y" and the absolute-difference check both work for negative integers. The LeetCode constraint limits to [0, 1000] but the logic generalizes.(x1,y1) and (x2,y2), the other two are at (x1+dy, y1−dx) and (x2+dy, y2−dx) where dx=x2−x1, dy=y2−y1. Same multiply-count idea applies.class DetectSquares {
// Map from "x,y" -> count of times that point was added
private pts = new Map<string, number>();
// Map from x-coordinate -> set of distinct y-values at that x
private xs = new Map<number, Set<number>>();
add(point: [number, number]): void {
const [x, y] = point;
const key = `${x},${y}`;
this.pts.set(key, (this.pts.get(key) ?? 0) + 1);
if (!this.xs.has(x)) this.xs.set(x, new Set());
this.xs.get(x)!.add(y);
}
count(point: [number, number]): number {
const [px, py] = point;
let total = 0;
// Iterate every candidate for the diagonal corner
for (const [ax, ays] of this.xs) {
if (ax === px) continue; // must be a different column
const d = ax - px; // side length (signed)
for (const ay of ays) {
if (Math.abs(ay - py) !== Math.abs(d)) continue; // must form a square
// The four corners are (px,py), (ax,ay), (px,ay), (ax,py)
const c1 = this.pts.get(`${ax},${ay}`) ?? 0; // diagonal
const c2 = this.pts.get(`${px},${ay}`) ?? 0; // same col as query
const c3 = this.pts.get(`${ax},${py}`) ?? 0; // same row as query
total += c1 * c2 * c3;
}
}
return total;
}
}add: O(1). count: O(n) where n = distinct stored points — the double loop visits every point exactly once. Space: O(n) for both maps.On each count, scan all pairs of stored points, keep the ones that form an axis-aligned square with the query point, and tally — no column index, just nested loops over the raw point list.
class DetectSquares {
// Just keep every point added, with a count map for O(1) corner lookups.
private points: [number, number][] = [];
private pts = new Map<string, number>();
add(point: [number, number]): void {
const [x, y] = point;
this.points.push([x, y]);
const key = x + ',' + y;
this.pts.set(key, (this.pts.get(key) ?? 0) + 1);
}
count(point: [number, number]): number {
const [px, py] = point;
let total = 0;
const seen = new Set<string>(); // dedupe distinct diagonal corners
// Try every stored point as the diagonal corner (ax, ay).
for (const [ax, ay] of this.points) {
if (ax === px || Math.abs(ax - px) !== Math.abs(ay - py)) continue;
const diagKey = ax + ',' + ay;
if (seen.has(diagKey)) continue;
seen.add(diagKey);
const c1 = this.pts.get(diagKey) ?? 0; // diagonal
const c2 = this.pts.get(px + ',' + ay) ?? 0; // same col as query
const c3 = this.pts.get(ax + ',' + py) ?? 0; // same row as query
total += c1 * c2 * c3;
}
return total;
}
}count rescans the entire history, so a run of q queries over n points costs O(q·n)— quadratic when both grow. The column-indexed Map<x, Set<y>> visits the same candidates without the duplicate-point rescans and dedupe bookkeeping.| "count axis-aligned squares with a given corner" | fix diagonal → multiply 3 corner counts |
| design: add points + query rectangles/squares | pts map + column-indexed xs map |
| |dx| = |dy| ≠ 0 condition | enforces equal side lengths → square |
| duplicate points increase count multiplicatively | store count per key, multiply in product |
class DetectSquares {
private pts = new Map<string, number>();
private xs = new Map<number, Set<number>>();
add([x, y]: [number, number]): void {
const key = `${x},${y}`;
this.pts.set(key, (this.pts.get(key) ?? 0) + 1);
if (!this.xs.has(x)) this.xs.set(x, new Set());
this.xs.get(x)!.add(y);
}
count([px, py]: [number, number]): number {
let total = 0;
for (const [ax, ays] of this.xs) {
if (ax === px) continue;
const d = ax - px;
for (const ay of ays) {
if (Math.abs(ay - py) !== Math.abs(d)) continue;
total += (this.pts.get(`${ax},${ay}`) ?? 0)
* (this.pts.get(`${px},${ay}`) ?? 0)
* (this.pts.get(`${ax},${py}`) ?? 0);
}
}
return total;
}
}(ax, ay). The query point is the anchor; the other two corners (px, ay) and (ax, py) are derived and looked up directly.add([3,10]), add([11,2]), add([3,2]), what does count([11,10]) return?(ax, ay) must satisfy for query point (px, py)?add([5,5]) is called twice, how does that affect count() calls that use (5,5) as a corner?count method, which three corners are looked up in pts?