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 n rectangles represented by a 0-indexed 2D integer array rectangles, where rectangles[i] = [widthi, heighti] denotes the width and height of the ith rectangle.
Two rectangles i and j (i < j) are considered interchangeable if they have the same width-to-height ratio. More formally, two rectangles are interchangeable if widthi/heighti == widthj/heightj (using decimal division, not integer division).
Return the number of pairs of interchangeable rectangles in rectangles.
Example 1:
Input: rectangles = [[4,8],[3,6],[10,20],[15,30]] Output: 6 Explanation: The following are the interchangeable pairs of rectangles by index (0-indexed): - Rectangle 0 with rectangle 1: 4/8 == 3/6. - Rectangle 0 with rectangle 2: 4/8 == 10/20. - Rectangle 0 with rectangle 3: 4/8 == 15/30. - Rectangle 1 with rectangle 2: 3/6 == 10/20. - Rectangle 1 with rectangle 3: 3/6 == 15/30. - Rectangle 2 with rectangle 3: 10/20 == 15/30.
Example 2:
Input: rectangles = [[4,5],[7,8]] Output: 0 Explanation: There are no interchangeable pairs of rectangles.
Constraints:
n == rectangles.length1 <= n <= 105rectangles[i].length == 21 <= widthi, heighti <= 105Problem summary: You are given n rectangles represented by a 0-indexed 2D integer array rectangles, where rectangles[i] = [widthi, heighti] denotes the width and height of the ith rectangle. Two rectangles i and j (i < j) are considered interchangeable if they have the same width-to-height ratio. More formally, two rectangles are interchangeable if widthi/heighti == widthj/heightj (using decimal division, not integer division). Return the number of pairs of interchangeable rectangles in rectangles.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Math
[[4,8],[3,6],[10,20],[15,30]]
[[4,5],[7,8]]
number-of-good-pairs)count-nice-pairs-in-an-array)replace-non-coprime-numbers-in-array)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2001: Number of Pairs of Interchangeable Rectangles
class Solution {
public long interchangeableRectangles(int[][] rectangles) {
long ans = 0;
int n = rectangles.length + 1;
Map<Long, Integer> cnt = new HashMap<>();
for (var e : rectangles) {
int w = e[0], h = e[1];
int g = gcd(w, h);
w /= g;
h /= g;
long x = (long) w * n + h;
ans += cnt.getOrDefault(x, 0);
cnt.merge(x, 1, Integer::sum);
}
return ans;
}
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
// Accepted solution for LeetCode #2001: Number of Pairs of Interchangeable Rectangles
func interchangeableRectangles(rectangles [][]int) int64 {
ans := 0
n := len(rectangles)
cnt := map[int]int{}
for _, e := range rectangles {
w, h := e[0], e[1]
g := gcd(w, h)
w, h = w/g, h/g
x := w*(n+1) + h
ans += cnt[x]
cnt[x]++
}
return int64(ans)
}
func gcd(a, b int) int {
if b == 0 {
return a
}
return gcd(b, a%b)
}
# Accepted solution for LeetCode #2001: Number of Pairs of Interchangeable Rectangles
class Solution:
def interchangeableRectangles(self, rectangles: List[List[int]]) -> int:
ans = 0
cnt = Counter()
for w, h in rectangles:
g = gcd(w, h)
w, h = w // g, h // g
ans += cnt[(w, h)]
cnt[(w, h)] += 1
return ans
// Accepted solution for LeetCode #2001: Number of Pairs of Interchangeable Rectangles
use std::collections::HashMap;
impl Solution {
pub fn compute_gcd(mut a: i32, mut b: i32) -> i32 {
while a > 0 && b > 0 {
if a > b {
a %= b;
} else {
b %= a;
}
}
if a == 0 {
b
} else {
a
}
}
pub fn interchangeable_rectangles(rectangles: Vec<Vec<i32>>) -> i64 {
rectangles
.into_iter()
.map(|v| {
let gcd = Solution::compute_gcd(v[0], v[1]);
(v[0] / gcd, v[1] / gcd)
})
.fold(HashMap::new(), |mut dict, t| {
dict.entry(t).and_modify(|cnt| *cnt += 1).or_insert(1);
dict
})
.into_iter()
.filter(|(_, v)| *v > 1)
.map(|(_, v)| v)
.fold(0, |mut cnt, v| {
cnt += v * (v - 1) / 2;
cnt
})
}
}
// Accepted solution for LeetCode #2001: Number of Pairs of Interchangeable Rectangles
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #2001: Number of Pairs of Interchangeable Rectangles
// class Solution {
// public long interchangeableRectangles(int[][] rectangles) {
// long ans = 0;
// int n = rectangles.length + 1;
// Map<Long, Integer> cnt = new HashMap<>();
// for (var e : rectangles) {
// int w = e[0], h = e[1];
// int g = gcd(w, h);
// w /= g;
// h /= g;
// long x = (long) w * n + h;
// ans += cnt.getOrDefault(x, 0);
// cnt.merge(x, 1, Integer::sum);
// }
// return ans;
// }
//
// private int gcd(int a, int b) {
// return b == 0 ? a : gcd(b, a % b);
// }
// }
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
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.
Wrong move: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.