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.
Design an algorithm that accepts a stream of integers and retrieves the product of the last k integers of the stream.
Implement the ProductOfNumbers class:
ProductOfNumbers() Initializes the object with an empty stream.void add(int num) Appends the integer num to the stream.int getProduct(int k) Returns the product of the last k numbers in the current list. You can assume that always the current list has at least k numbers.The test cases are generated so that, at any time, the product of any contiguous sequence of numbers will fit into a single 32-bit integer without overflowing.
Example:
Input ["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"] [[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]] Output [null,null,null,null,null,null,20,40,0,null,32] Explanation ProductOfNumbers productOfNumbers = new ProductOfNumbers(); productOfNumbers.add(3); // [3] productOfNumbers.add(0); // [3,0] productOfNumbers.add(2); // [3,0,2] productOfNumbers.add(5); // [3,0,2,5] productOfNumbers.add(4); // [3,0,2,5,4] productOfNumbers.getProduct(2); // return 20. The product of the last 2 numbers is 5 * 4 = 20 productOfNumbers.getProduct(3); // return 40. The product of the last 3 numbers is 2 * 5 * 4 = 40 productOfNumbers.getProduct(4); // return 0. The product of the last 4 numbers is 0 * 2 * 5 * 4 = 0 productOfNumbers.add(8); // [3,0,2,5,4,8] productOfNumbers.getProduct(2); // return 32. The product of the last 2 numbers is 4 * 8 = 32
Constraints:
0 <= num <= 1001 <= k <= 4 * 1044 * 104 calls will be made to add and getProduct.GetProduct and Add to work in O(1) time complexity instead of O(k) time complexity?Problem summary: Design an algorithm that accepts a stream of integers and retrieves the product of the last k integers of the stream. Implement the ProductOfNumbers class: ProductOfNumbers() Initializes the object with an empty stream. void add(int num) Appends the integer num to the stream. int getProduct(int k) Returns the product of the last k numbers in the current list. You can assume that always the current list has at least k numbers. The test cases are generated so that, at any time, the product of any contiguous sequence of numbers will fit into a single 32-bit integer without overflowing.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Math · Design
["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"] [[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1352: Product of the Last K Numbers
class ProductOfNumbers {
private List<Integer> s = new ArrayList<>();
public ProductOfNumbers() {
s.add(1);
}
public void add(int num) {
if (num == 0) {
s.clear();
s.add(1);
return;
}
s.add(s.get(s.size() - 1) * num);
}
public int getProduct(int k) {
int n = s.size();
return n <= k ? 0 : s.get(n - 1) / s.get(n - k - 1);
}
}
/**
* Your ProductOfNumbers object will be instantiated and called as such:
* ProductOfNumbers obj = new ProductOfNumbers();
* obj.add(num);
* int param_2 = obj.getProduct(k);
*/
// Accepted solution for LeetCode #1352: Product of the Last K Numbers
type ProductOfNumbers struct {
s []int
}
func Constructor() ProductOfNumbers {
return ProductOfNumbers{[]int{1}}
}
func (this *ProductOfNumbers) Add(num int) {
if num == 0 {
this.s = []int{1}
return
}
this.s = append(this.s, this.s[len(this.s)-1]*num)
}
func (this *ProductOfNumbers) GetProduct(k int) int {
n := len(this.s)
if n <= k {
return 0
}
return this.s[len(this.s)-1] / this.s[len(this.s)-k-1]
}
/**
* Your ProductOfNumbers object will be instantiated and called as such:
* obj := Constructor();
* obj.Add(num);
* param_2 := obj.GetProduct(k);
*/
# Accepted solution for LeetCode #1352: Product of the Last K Numbers
class ProductOfNumbers:
def __init__(self):
self.s = [1]
def add(self, num: int) -> None:
if num == 0:
self.s = [1]
return
self.s.append(self.s[-1] * num)
def getProduct(self, k: int) -> int:
return 0 if len(self.s) <= k else self.s[-1] // self.s[-k - 1]
# Your ProductOfNumbers object will be instantiated and called as such:
# obj = ProductOfNumbers()
# obj.add(num)
# param_2 = obj.getProduct(k)
// Accepted solution for LeetCode #1352: Product of the Last K Numbers
#[derive(Default)]
struct ProductOfNumbers {
prefix: Vec<i32>,
}
impl ProductOfNumbers {
fn new() -> Self {
let prefix = vec![1];
ProductOfNumbers { prefix }
}
fn add(&mut self, num: i32) {
if num > 0 {
let prev = self.prefix[self.prefix.len() - 1];
self.prefix.push(prev * num);
} else {
self.prefix = vec![1];
}
}
fn get_product(&self, k: i32) -> i32 {
let k = k as usize;
let n = self.prefix.len();
if k >= n {
0
} else {
self.prefix[n - 1] / self.prefix[n - 1 - k]
}
}
}
#[test]
fn test() {
let mut obj = ProductOfNumbers::new();
obj.add(3);
obj.add(0);
obj.add(2);
obj.add(5);
obj.add(4);
assert_eq!(obj.get_product(2), 20);
assert_eq!(obj.get_product(3), 40);
assert_eq!(obj.get_product(4), 0);
obj.add(8);
assert_eq!(obj.get_product(2), 32);
}
// Accepted solution for LeetCode #1352: Product of the Last K Numbers
class ProductOfNumbers {
s = [1];
add(num: number): void {
if (num === 0) {
this.s = [1];
} else {
const i = this.s.length;
this.s[i] = this.s[i - 1] * num;
}
}
getProduct(k: number): number {
const i = this.s.length;
if (k > i - 1) return 0;
return this.s[i - 1] / this.s[i - k - 1];
}
}
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: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.