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.
Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards.
Given an integer array hand where hand[i] is the value written on the ith card and an integer groupSize, return true if she can rearrange the cards, or false otherwise.
Example 1:
Input: hand = [1,2,3,6,2,3,4,7,8], groupSize = 3 Output: true Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8]
Example 2:
Input: hand = [1,2,3,4,5], groupSize = 4 Output: false Explanation: Alice's hand can not be rearranged into groups of 4.
Constraints:
1 <= hand.length <= 1040 <= hand[i] <= 1091 <= groupSize <= hand.lengthNote: This question is the same as 1296: https://leetcode.com/problems/divide-array-in-sets-of-k-consecutive-numbers/
Problem summary: Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards. Given an integer array hand where hand[i] is the value written on the ith card and an integer groupSize, return true if she can rearrange the cards, or false otherwise.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Greedy
[1,2,3,6,2,3,4,7,8] 3
[1,2,3,4,5] 4
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #846: Hand of Straights
class Solution {
public boolean isNStraightHand(int[] hand, int groupSize) {
if (hand.length % groupSize != 0) {
return false;
}
Arrays.sort(hand);
Map<Integer, Integer> cnt = new HashMap<>();
for (int x : hand) {
cnt.merge(x, 1, Integer::sum);
}
for (int x : hand) {
if (cnt.getOrDefault(x, 0) > 0) {
for (int y = x; y < x + groupSize; ++y) {
if (cnt.merge(y, -1, Integer::sum) < 0) {
return false;
}
}
}
}
return true;
}
}
// Accepted solution for LeetCode #846: Hand of Straights
func isNStraightHand(hand []int, groupSize int) bool {
if len(hand)%groupSize != 0 {
return false
}
sort.Ints(hand)
cnt := map[int]int{}
for _, x := range hand {
cnt[x]++
}
for _, x := range hand {
if cnt[x] > 0 {
for y := x; y < x+groupSize; y++ {
if cnt[y] == 0 {
return false
}
cnt[y]--
}
}
}
return true
}
# Accepted solution for LeetCode #846: Hand of Straights
class Solution:
def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
if len(hand) % groupSize:
return False
cnt = Counter(hand)
for x in sorted(hand):
if cnt[x]:
for y in range(x, x + groupSize):
if cnt[y] == 0:
return False
cnt[y] -= 1
return True
// Accepted solution for LeetCode #846: Hand of Straights
struct Solution;
use std::collections::BTreeMap;
use std::collections::VecDeque;
use std::iter::FromIterator;
impl Solution {
fn is_n_straight_hand(hand: Vec<i32>, w: i32) -> bool {
let mut btm: BTreeMap<i32, usize> = BTreeMap::new();
for card in hand {
*btm.entry(card).or_default() += 1;
}
let w = w as usize;
let mut queue: VecDeque<(i32, usize)> = VecDeque::from_iter(btm);
while !queue.is_empty() {
let first = queue.pop_front().unwrap();
let mut stack: Vec<(i32, usize)> = vec![];
for i in 1..w {
if let Some(front) = queue.pop_front() {
if front.0 != first.0 + i as i32 || front.1 < first.1 {
return false;
} else {
let left = front.1 - first.1;
if left > 0 {
stack.push((front.0, left));
}
}
} else {
return false;
}
}
while let Some(last) = stack.pop() {
queue.push_front(last);
}
}
true
}
}
#[test]
fn test() {
let hand = vec![1, 2, 3, 6, 2, 3, 4, 7, 8];
let w = 3;
let res = true;
assert_eq!(Solution::is_n_straight_hand(hand, w), res);
let hand = vec![1, 2, 3, 4, 5];
let w = 4;
let res = false;
assert_eq!(Solution::is_n_straight_hand(hand, w), res);
let hand = vec![5, 1];
let w = 2;
let res = false;
assert_eq!(Solution::is_n_straight_hand(hand, w), res);
}
// Accepted solution for LeetCode #846: Hand of Straights
function isNStraightHand(hand: number[], groupSize: number): boolean {
if (hand.length % groupSize !== 0) {
return false;
}
const cnt = new Map<number, number>();
for (const x of hand) {
cnt.set(x, (cnt.get(x) || 0) + 1);
}
hand.sort((a, b) => a - b);
for (const x of hand) {
if (cnt.get(x)! > 0) {
for (let y = x; y < x + groupSize; y++) {
if ((cnt.get(y) || 0) === 0) {
return false;
}
cnt.set(y, cnt.get(y)! - 1);
}
}
}
return true;
}
Use this to step through a reusable interview workflow for this problem.
Try every possible combination of choices. With n items each having two states (include/exclude), the search space is 2ⁿ. Evaluating each combination takes O(n), giving O(n × 2ⁿ). The recursion stack or subset storage uses O(n) space.
Greedy algorithms typically sort the input (O(n log n)) then make a single pass (O(n)). The sort dominates. If the input is already sorted or the greedy choice can be computed without sorting, time drops to O(n). Proving greedy correctness (exchange argument) is harder than the implementation.
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: Locally optimal choices may fail globally.
Usually fails on: Counterexamples appear on crafted input orderings.
Fix: Verify with exchange argument or monotonic objective before committing.