Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
You are given a stream of points on the X-Y plane. Design an algorithm that:
An axis-aligned square is a square whose edges are all the same length and are either parallel or perpendicular to the x-axis and y-axis.
Implement the DetectSquares class:
DetectSquares() Initializes the object with an empty data structure.void add(int[] point) Adds a new point point = [x, y] to the data structure.int count(int[] point) Counts the number of ways to form axis-aligned squares with point point = [x, y] as described above.Example 1:
Input
["DetectSquares", "add", "add", "add", "count", "count", "add", "count"]
[[], [[3, 10]], [[11, 2]], [[3, 2]], [[11, 10]], [[14, 8]], [[11, 2]], [[11, 10]]]
Output
[null, null, null, null, 1, 0, null, 2]
Explanation
DetectSquares detectSquares = new DetectSquares();
detectSquares.add([3, 10]);
detectSquares.add([11, 2]);
detectSquares.add([3, 2]);
detectSquares.count([11, 10]); // return 1. You can choose:
// - The first, second, and third points
detectSquares.count([14, 8]); // return 0. The query point cannot form a square with any points in the data structure.
detectSquares.add([11, 2]); // Adding duplicate points is allowed.
detectSquares.count([11, 10]); // return 2. You can choose:
// - The first, second, and third points
// - The first, third, and fourth points
Constraints:
point.length == 20 <= x, y <= 10003000 calls in total will be made to add and count.Problem summary: You are given a stream of points on the X-Y plane. Design an algorithm that: Adds new points from the stream into a data structure. Duplicate points are allowed and should be treated as different points. Given a query point, counts the number of ways to choose three points from the data structure such that the three points and the query point form an axis-aligned square with positive area. An axis-aligned square is a square whose edges are all the same length and are either parallel or perpendicular to the x-axis and y-axis. Implement the DetectSquares class: DetectSquares() Initializes the object with an empty data structure. void add(int[] point) Adds a new point point = [x, y] to the data structure. int count(int[] point) Counts the number of ways to form axis-aligned squares with point point = [x, y] as described above.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Design
["DetectSquares","add","add","add","count","count","add","count"] [[],[[3,10]],[[11,2]],[[3,2]],[[11,10]],[[14,8]],[[11,2]],[[11,10]]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2013: Detect Squares
class DetectSquares {
private Map<Integer, Map<Integer, Integer>> cnt = new HashMap<>();
public DetectSquares() {
}
public void add(int[] point) {
int x = point[0], y = point[1];
cnt.computeIfAbsent(x, k -> new HashMap<>()).merge(y, 1, Integer::sum);
}
public int count(int[] point) {
int x1 = point[0], y1 = point[1];
if (!cnt.containsKey(x1)) {
return 0;
}
int ans = 0;
for (var e : cnt.entrySet()) {
int x2 = e.getKey();
if (x2 != x1) {
int d = x2 - x1;
var cnt1 = cnt.get(x1);
var cnt2 = e.getValue();
ans += cnt2.getOrDefault(y1, 0) * cnt1.getOrDefault(y1 + d, 0)
* cnt2.getOrDefault(y1 + d, 0);
ans += cnt2.getOrDefault(y1, 0) * cnt1.getOrDefault(y1 - d, 0)
* cnt2.getOrDefault(y1 - d, 0);
}
}
return ans;
}
}
/**
* Your DetectSquares object will be instantiated and called as such:
* DetectSquares obj = new DetectSquares();
* obj.add(point);
* int param_2 = obj.count(point);
*/
// Accepted solution for LeetCode #2013: Detect Squares
type DetectSquares struct {
cnt map[int]map[int]int
}
func Constructor() DetectSquares {
return DetectSquares{map[int]map[int]int{}}
}
func (this *DetectSquares) Add(point []int) {
x, y := point[0], point[1]
if _, ok := this.cnt[x]; !ok {
this.cnt[x] = map[int]int{}
}
this.cnt[x][y]++
}
func (this *DetectSquares) Count(point []int) (ans int) {
x1, y1 := point[0], point[1]
if cnt1, ok := this.cnt[x1]; ok {
for x2, cnt2 := range this.cnt {
if x2 != x1 {
d := x2 - x1
ans += cnt2[y1] * cnt1[y1+d] * cnt2[y1+d]
ans += cnt2[y1] * cnt1[y1-d] * cnt2[y1-d]
}
}
}
return
}
/**
* Your DetectSquares object will be instantiated and called as such:
* obj := Constructor();
* obj.Add(point);
* param_2 := obj.Count(point);
*/
# Accepted solution for LeetCode #2013: Detect Squares
class DetectSquares:
def __init__(self):
self.cnt = defaultdict(Counter)
def add(self, point: List[int]) -> None:
x, y = point
self.cnt[x][y] += 1
def count(self, point: List[int]) -> int:
x1, y1 = point
if x1 not in self.cnt:
return 0
ans = 0
for x2 in self.cnt.keys():
if x2 != x1:
d = x2 - x1
ans += self.cnt[x2][y1] * self.cnt[x1][y1 + d] * self.cnt[x2][y1 + d]
ans += self.cnt[x2][y1] * self.cnt[x1][y1 - d] * self.cnt[x2][y1 - d]
return ans
# Your DetectSquares object will be instantiated and called as such:
# obj = DetectSquares()
# obj.add(point)
# param_2 = obj.count(point)
// Accepted solution for LeetCode #2013: Detect Squares
use std::collections::HashMap;
struct DetectSquares {
points: Vec<(i32, i32)>,
counts: HashMap<(i32, i32), i32>
}
impl DetectSquares {
fn new() -> Self {
Self {
points: vec![],
counts: HashMap::new()
}
}
fn add(&mut self, point: Vec<i32>) {
let p = (point[0], point[1]);
self.points.push(p);
*self.counts.entry(p).or_default() += 1;
}
fn count(&self, point: Vec<i32>) -> i32 {
let mut res = 0;
let (px, py) = (point[0], point[1]);
for (x, y) in self.points.iter() {
if (py - y).abs() != (px - x).abs() || *x == px || *y == py {
continue;
}
res += self.counts.get(&(*x, py)).unwrap_or(&0) * self.counts.get(&(px,*y)).unwrap_or(&0);
}
res
}
}
// Accepted solution for LeetCode #2013: Detect Squares
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #2013: Detect Squares
// class DetectSquares {
// private Map<Integer, Map<Integer, Integer>> cnt = new HashMap<>();
//
// public DetectSquares() {
// }
//
// public void add(int[] point) {
// int x = point[0], y = point[1];
// cnt.computeIfAbsent(x, k -> new HashMap<>()).merge(y, 1, Integer::sum);
// }
//
// public int count(int[] point) {
// int x1 = point[0], y1 = point[1];
// if (!cnt.containsKey(x1)) {
// return 0;
// }
// int ans = 0;
// for (var e : cnt.entrySet()) {
// int x2 = e.getKey();
// if (x2 != x1) {
// int d = x2 - x1;
// var cnt1 = cnt.get(x1);
// var cnt2 = e.getValue();
// ans += cnt2.getOrDefault(y1, 0) * cnt1.getOrDefault(y1 + d, 0)
// * cnt2.getOrDefault(y1 + d, 0);
// ans += cnt2.getOrDefault(y1, 0) * cnt1.getOrDefault(y1 - d, 0)
// * cnt2.getOrDefault(y1 - d, 0);
// }
// }
// return ans;
// }
// }
//
// /**
// * Your DetectSquares object will be instantiated and called as such:
// * DetectSquares obj = new DetectSquares();
// * obj.add(point);
// * int param_2 = obj.count(point);
// */
Use this to step through a reusable interview workflow for this problem.
Use a simple list or array for storage. Each operation (get, put, remove) requires a linear scan to find the target element — O(n) per operation. Space is O(n) to store the data. The linear search makes this impractical for frequent operations.
Design problems target O(1) amortized per operation by combining data structures (hash map + doubly-linked list for LRU, stack + min-tracking for MinStack). Space is always at least O(n) to store the data. The challenge is achieving constant-time operations through clever structure composition.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.