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.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Given a function fn, return a memoized version of that function.
A memoized function is a function that will never be called twice with the same inputs. Instead it will return a cached value.
fn can be any function and there are no constraints on what type of values it accepts. Inputs are considered identical if they are === to each other.
Example 1:
Input:
getInputs = () => [[2,2],[2,2],[1,2]]
fn = function (a, b) { return a + b; }
Output: [{"val":4,"calls":1},{"val":4,"calls":1},{"val":3,"calls":2}]
Explanation:
const inputs = getInputs();
const memoized = memoize(fn);
for (const arr of inputs) {
memoized(...arr);
}
For the inputs of (2, 2): 2 + 2 = 4, and it required a call to fn().
For the inputs of (2, 2): 2 + 2 = 4, but those inputs were seen before so no call to fn() was required.
For the inputs of (1, 2): 1 + 2 = 3, and it required another call to fn() for a total of 2.
Example 2:
Input:
getInputs = () => [[{},{}],[{},{}],[{},{}]]
fn = function (a, b) { return ({...a, ...b}); }
Output: [{"val":{},"calls":1},{"val":{},"calls":2},{"val":{},"calls":3}]
Explanation:
Merging two empty objects will always result in an empty object. It may seem like there should only be 1 call to fn() because of cache-hits, however none of those objects are === to each other.
Example 3:
Input:
getInputs = () => { const o = {}; return [[o,o],[o,o],[o,o]]; }
fn = function (a, b) { return ({...a, ...b}); }
Output: [{"val":{},"calls":1},{"val":{},"calls":1},{"val":{},"calls":1}]
Explanation:
Merging two empty objects will always result in an empty object. The 2nd and 3rd third function calls result in a cache-hit. This is because every object passed in is identical.
Constraints:
1 <= inputs.length <= 1050 <= inputs.flat().length <= 105inputs[i][j] != NaNProblem summary: Given a function fn, return a memoized version of that function. A memoized function is a function that will never be called twice with the same inputs. Instead it will return a cached value. fn can be any function and there are no constraints on what type of values it accepts. Inputs are considered identical if they are === to each other.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: General problem-solving
() => [[2,2],[2,2],[1,2]]
function (a, b) { return a + b; }() => [[{},{}],[{},{}],[{},{}]]
function (a, b) { return ({...a, ...b}); }() => { const o = {}; return [[o,o],[o,o],[o,o]]; }
function (a, b) { return ({...a, ...b}); }memoize)curry)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2630: Memoize II
// Auto-generated Java example from ts.
class Solution {
public void exampleSolution() {
}
}
// Reference (ts):
// // Accepted solution for LeetCode #2630: Memoize II
// type Fn = (...params: any) => any;
//
// function memoize(fn: Fn): Fn {
// const idxMap: Map<string, number> = new Map();
// const cache: Map<string, any> = new Map();
//
// const getIdx = (obj: any): number => {
// if (!idxMap.has(obj)) {
// idxMap.set(obj, idxMap.size);
// }
// return idxMap.get(obj)!;
// };
//
// return function (...params: any) {
// const key = params.map(getIdx).join(',');
// if (!cache.has(key)) {
// cache.set(key, fn(...params));
// }
// return cache.get(key)!;
// };
// }
//
// /**
// * let callCount = 0;
// * const memoizedFn = memoize(function (a, b) {
// * callCount += 1;
// * return a + b;
// * })
// * memoizedFn(2, 3) // 5
// * memoizedFn(2, 3) // 5
// * console.log(callCount) // 1
// */
// Accepted solution for LeetCode #2630: Memoize II
// Auto-generated Go example from ts.
func exampleSolution() {
}
// Reference (ts):
// // Accepted solution for LeetCode #2630: Memoize II
// type Fn = (...params: any) => any;
//
// function memoize(fn: Fn): Fn {
// const idxMap: Map<string, number> = new Map();
// const cache: Map<string, any> = new Map();
//
// const getIdx = (obj: any): number => {
// if (!idxMap.has(obj)) {
// idxMap.set(obj, idxMap.size);
// }
// return idxMap.get(obj)!;
// };
//
// return function (...params: any) {
// const key = params.map(getIdx).join(',');
// if (!cache.has(key)) {
// cache.set(key, fn(...params));
// }
// return cache.get(key)!;
// };
// }
//
// /**
// * let callCount = 0;
// * const memoizedFn = memoize(function (a, b) {
// * callCount += 1;
// * return a + b;
// * })
// * memoizedFn(2, 3) // 5
// * memoizedFn(2, 3) // 5
// * console.log(callCount) // 1
// */
# Accepted solution for LeetCode #2630: Memoize II
# Auto-generated Python example from ts.
def example_solution() -> None:
return
# Reference (ts):
# // Accepted solution for LeetCode #2630: Memoize II
# type Fn = (...params: any) => any;
#
# function memoize(fn: Fn): Fn {
# const idxMap: Map<string, number> = new Map();
# const cache: Map<string, any> = new Map();
#
# const getIdx = (obj: any): number => {
# if (!idxMap.has(obj)) {
# idxMap.set(obj, idxMap.size);
# }
# return idxMap.get(obj)!;
# };
#
# return function (...params: any) {
# const key = params.map(getIdx).join(',');
# if (!cache.has(key)) {
# cache.set(key, fn(...params));
# }
# return cache.get(key)!;
# };
# }
#
# /**
# * let callCount = 0;
# * const memoizedFn = memoize(function (a, b) {
# * callCount += 1;
# * return a + b;
# * })
# * memoizedFn(2, 3) // 5
# * memoizedFn(2, 3) // 5
# * console.log(callCount) // 1
# */
// Accepted solution for LeetCode #2630: Memoize II
// Rust example auto-generated from ts reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (ts):
// // Accepted solution for LeetCode #2630: Memoize II
// type Fn = (...params: any) => any;
//
// function memoize(fn: Fn): Fn {
// const idxMap: Map<string, number> = new Map();
// const cache: Map<string, any> = new Map();
//
// const getIdx = (obj: any): number => {
// if (!idxMap.has(obj)) {
// idxMap.set(obj, idxMap.size);
// }
// return idxMap.get(obj)!;
// };
//
// return function (...params: any) {
// const key = params.map(getIdx).join(',');
// if (!cache.has(key)) {
// cache.set(key, fn(...params));
// }
// return cache.get(key)!;
// };
// }
//
// /**
// * let callCount = 0;
// * const memoizedFn = memoize(function (a, b) {
// * callCount += 1;
// * return a + b;
// * })
// * memoizedFn(2, 3) // 5
// * memoizedFn(2, 3) // 5
// * console.log(callCount) // 1
// */
// Accepted solution for LeetCode #2630: Memoize II
type Fn = (...params: any) => any;
function memoize(fn: Fn): Fn {
const idxMap: Map<string, number> = new Map();
const cache: Map<string, any> = new Map();
const getIdx = (obj: any): number => {
if (!idxMap.has(obj)) {
idxMap.set(obj, idxMap.size);
}
return idxMap.get(obj)!;
};
return function (...params: any) {
const key = params.map(getIdx).join(',');
if (!cache.has(key)) {
cache.set(key, fn(...params));
}
return cache.get(key)!;
};
}
/**
* let callCount = 0;
* const memoizedFn = memoize(function (a, b) {
* callCount += 1;
* return a + b;
* })
* memoizedFn(2, 3) // 5
* memoizedFn(2, 3) // 5
* console.log(callCount) // 1
*/
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.