points the number in a cell is ptsCount · ring = query point · outline = the square being tested
class DetectSquares { private Map<Integer, Integer> ptsCount; // key = x * 1001 + y private List<int[]> pts; public DetectSquares() { ptsCount = new HashMap<>(); pts = new ArrayList<>(); } public void add(int[] point) { int key = point[0] * 1001 + point[1]; ptsCount.merge(key, 1, Integer::sum); pts.add(point); } public int count(int[] point) { int res = 0; int px = point[0]; int py = point[1]; for (int[] p : pts) { int x = p[0]; int y = p[1]; int dx = Math.abs(px - x); int dy = Math.abs(py - y); if (dx != dy || x == px || y == py) { continue; } int a = ptsCount.getOrDefault(x * 1001 + py, 0); int b = ptsCount.getOrDefault(px * 1001 + y, 0); res += a * b; } return res; }}
- call
- —
- pts.size()
- 0
- res
- —
- returns
- —
Why it works
- Brute force tries every triple of stored points with the query: O(n³) per count.
- The diagonal decides everything: in an axis-aligned square, the corner opposite the query point
(px, py)is some(x, y)with|x − px| = |y − py| ≠ 0. Once it is fixed, the other two corners must be exactly(x, py)and(px, y). So loop over stored points as diagonal candidates and look the other two up. - Counts handle duplicates: each copy of the diagonal point appears in
ptsand is visited once; the other two corners contributea × bways, whereaandbare their counts. Three copies of a corner make three different squares. - Cost:
addis O(1);countis O(n) over the stored list, each lookup O(1) in the hash map. Space O(n). - Pitfall: skip candidates in the query's row or column (
x == pxory == py): they give zero-area "squares". And a rectangle with unequal sides must not count, hence the|dx| = |dy|test.