204 lines
9.0 KiB
YAML
204 lines
9.0 KiB
YAML
title: Best Poker Hand
|
|
slug: best-poker-hand
|
|
difficulty: easy
|
|
leetcode_id: 2347
|
|
leetcode_url: https://leetcode.com/problems/best-poker-hand/
|
|
categories:
|
|
- arrays
|
|
- hash-tables
|
|
patterns:
|
|
- greedy
|
|
|
|
function_signature: "def best_hand(ranks: list[int], suits: list[str]) -> str:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { ranks: [13, 2, 3, 1, 9], suits: ["a", "a", "a", "a", "a"] }
|
|
expected: "Flush"
|
|
- input: { ranks: [4, 4, 2, 4, 4], suits: ["d", "a", "a", "b", "c"] }
|
|
expected: "Three of a Kind"
|
|
- input: { ranks: [10, 10, 2, 12, 9], suits: ["a", "b", "c", "a", "d"] }
|
|
expected: "Pair"
|
|
hidden:
|
|
- input: { ranks: [1, 2, 3, 4, 5], suits: ["a", "b", "c", "d", "a"] }
|
|
expected: "High Card"
|
|
- input: { ranks: [3, 3, 3, 3, 5], suits: ["a", "b", "c", "d", "a"] }
|
|
expected: "Three of a Kind"
|
|
- input: { ranks: [1, 1, 2, 2, 3], suits: ["a", "b", "c", "d", "a"] }
|
|
expected: "Pair"
|
|
- input: { ranks: [7, 7, 7, 7, 7], suits: ["a", "a", "a", "a", "a"] }
|
|
expected: "Flush"
|
|
- input: { ranks: [13, 13, 13, 1, 1], suits: ["b", "c", "d", "a", "b"] }
|
|
expected: "Three of a Kind"
|
|
- input: { ranks: [2, 4, 6, 8, 10], suits: ["d", "d", "d", "d", "d"] }
|
|
expected: "Flush"
|
|
|
|
description: |
|
|
You are given an integer array `ranks` and a character array `suits`. You have `5` cards where the i<sup>th</sup> card has a rank of `ranks[i]` and a suit of `suits[i]`.
|
|
|
|
The following are the types of **poker hands** you can make from best to worst:
|
|
|
|
1. `"Flush"`: Five cards of the same suit.
|
|
2. `"Three of a Kind"`: Three cards of the same rank.
|
|
3. `"Pair"`: Two cards of the same rank.
|
|
4. `"High Card"`: Any single card.
|
|
|
|
Return *a string representing the **best** type of **poker hand** you can make with the given cards.*
|
|
|
|
**Note** that the return values are **case-sensitive**.
|
|
|
|
constraints: |
|
|
- `ranks.length == suits.length == 5`
|
|
- `1 <= ranks[i] <= 13`
|
|
- `'a' <= suits[i] <= 'd'`
|
|
- No two cards have the same rank and suit.
|
|
|
|
examples:
|
|
- input: 'ranks = [13,2,3,1,9], suits = ["a","a","a","a","a"]'
|
|
output: '"Flush"'
|
|
explanation: "The hand with all the cards consists of 5 cards with the same suit, so we have a \"Flush\"."
|
|
- input: 'ranks = [4,4,2,4,4], suits = ["d","a","a","b","c"]'
|
|
output: '"Three of a Kind"'
|
|
explanation: "The hand with the first, second, and fourth card consists of 3 cards with the same rank, so we have a \"Three of a Kind\". Note that we could also make a \"Pair\" hand but \"Three of a Kind\" is a better hand."
|
|
- input: 'ranks = [10,10,2,12,9], suits = ["a","b","c","a","d"]'
|
|
output: '"Pair"'
|
|
explanation: "The hand with the first and second card consists of 2 cards with the same rank, so we have a \"Pair\". Note that we cannot make a \"Flush\" or a \"Three of a Kind\"."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Think of this problem like a poker player evaluating their hand. You're dealt 5 cards and need to identify the **best possible hand** you can claim.
|
|
|
|
The key insight is that the hand types are ordered from best to worst, so we should check for them **in that order**. As soon as we find a match, we can return immediately — no need to check lesser hands.
|
|
|
|
For a Flush, we need all 5 cards to share the same suit. This is easy to check: if there's only one unique suit among all cards, it's a Flush.
|
|
|
|
For Three of a Kind or Pair, we need to count how many times each rank appears. The **maximum frequency** of any rank tells us our best option:
|
|
- If any rank appears 3+ times → "Three of a Kind"
|
|
- If any rank appears exactly 2 times → "Pair"
|
|
- Otherwise → "High Card"
|
|
|
|
The problem is simplified by the fixed hand size of 5 cards, which means we can use simple counting with hash tables.
|
|
|
|
approach: |
|
|
We solve this using a **Priority Check with Counting** approach:
|
|
|
|
**Step 1: Check for Flush**
|
|
|
|
- Count the unique suits in the `suits` array
|
|
- If there's only 1 unique suit, all 5 cards match → return `"Flush"`
|
|
- We can use a set to find unique elements efficiently
|
|
|
|
|
|
|
|
**Step 2: Count rank frequencies**
|
|
|
|
- Use a hash map (Counter/dictionary) to count occurrences of each rank
|
|
- Find the maximum frequency among all ranks
|
|
|
|
|
|
|
|
**Step 3: Determine hand based on max frequency**
|
|
|
|
- If `max_count >= 3` → return `"Three of a Kind"`
|
|
- If `max_count == 2` → return `"Pair"`
|
|
- Otherwise → return `"High Card"`
|
|
|
|
|
|
|
|
This greedy approach works because we check hands in order of priority. The moment we find a qualifying hand, we return it — guaranteeing we report the best possible hand.
|
|
|
|
common_pitfalls:
|
|
- title: Checking in Wrong Order
|
|
description: |
|
|
A common mistake is checking for Pair before Three of a Kind, or checking ranks before suits.
|
|
|
|
The problem states the hand types from best to worst. If you have four cards of the same rank (like `[4,4,4,4,2]`), that qualifies as both "Three of a Kind" and "Pair". You must return "Three of a Kind" because it's ranked higher.
|
|
|
|
Always check in priority order: Flush → Three of a Kind → Pair → High Card.
|
|
wrong_approach: "Check for Pair before Three of a Kind"
|
|
correct_approach: "Check hands in order of priority (best to worst)"
|
|
|
|
- title: Overcomplicating the Flush Check
|
|
description: |
|
|
Some solutions iterate through the suits array comparing elements when a simpler approach exists.
|
|
|
|
Since we have exactly 5 cards and 5 suits, checking if all suits are the same is equivalent to checking if there's only 1 unique suit. Using `len(set(suits)) == 1` is cleaner and more Pythonic.
|
|
wrong_approach: "Loop comparing suits[i] == suits[0] for all i"
|
|
correct_approach: "Use len(set(suits)) == 1"
|
|
|
|
- title: Missing the Four of a Kind Case
|
|
description: |
|
|
The problem doesn't list "Four of a Kind" as a separate hand type. When you have 4 cards of the same rank, it still only counts as "Three of a Kind" per the problem definition.
|
|
|
|
Don't add extra logic for four-of-a-kind — the `max_count >= 3` check handles it correctly by returning "Three of a Kind".
|
|
|
|
key_takeaways:
|
|
- "**Priority-based evaluation**: When categorising into ranked tiers, check from best to worst and return on first match"
|
|
- "**Hash tables for counting**: `Counter` or dictionaries make frequency counting trivial — finding max frequency is O(n)"
|
|
- "**Sets for uniqueness**: Converting to a set instantly tells you how many unique elements exist"
|
|
- "**Fixed input size**: With only 5 cards, even 'inefficient' approaches are fast — but clean code still matters"
|
|
|
|
time_complexity: "O(1). We process exactly 5 cards, making this effectively constant time regardless of implementation details."
|
|
space_complexity: "O(1). The hash map stores at most 5 entries (one per card), and the set stores at most 4 suits — both bounded by constants."
|
|
|
|
solutions:
|
|
- approach_name: Priority Check with Counting
|
|
is_optimal: true
|
|
code: |
|
|
from collections import Counter
|
|
|
|
def best_hand(ranks: list[int], suits: list[str]) -> str:
|
|
# Check for Flush: all 5 cards same suit
|
|
if len(set(suits)) == 1:
|
|
return "Flush"
|
|
|
|
# Count frequency of each rank
|
|
rank_counts = Counter(ranks)
|
|
max_count = max(rank_counts.values())
|
|
|
|
# Check for Three of a Kind (3 or more of same rank)
|
|
if max_count >= 3:
|
|
return "Three of a Kind"
|
|
|
|
# Check for Pair (exactly 2 of same rank)
|
|
if max_count == 2:
|
|
return "Pair"
|
|
|
|
# Default: High Card
|
|
return "High Card"
|
|
explanation: |
|
|
**Time Complexity:** O(1) — We always process exactly 5 cards.
|
|
|
|
**Space Complexity:** O(1) — Counter and set are bounded by the fixed hand size.
|
|
|
|
We check for each hand type in order of priority. The Flush check uses a set for O(1) uniqueness counting. The Counter gives us rank frequencies, and we only need the maximum to determine Three of a Kind vs Pair vs High Card.
|
|
|
|
- approach_name: Manual Counting (No Imports)
|
|
is_optimal: false
|
|
code: |
|
|
def best_hand(ranks: list[int], suits: list[str]) -> str:
|
|
# Check for Flush: compare all suits to the first
|
|
is_flush = all(s == suits[0] for s in suits)
|
|
if is_flush:
|
|
return "Flush"
|
|
|
|
# Count rank frequencies using a dictionary
|
|
rank_counts = {}
|
|
for rank in ranks:
|
|
rank_counts[rank] = rank_counts.get(rank, 0) + 1
|
|
|
|
# Find max frequency
|
|
max_count = max(rank_counts.values())
|
|
|
|
if max_count >= 3:
|
|
return "Three of a Kind"
|
|
if max_count == 2:
|
|
return "Pair"
|
|
return "High Card"
|
|
explanation: |
|
|
**Time Complexity:** O(1) — Fixed 5-card input.
|
|
|
|
**Space Complexity:** O(1) — Dictionary bounded by hand size.
|
|
|
|
This version avoids importing `Counter` by manually building a frequency dictionary. The `all()` function provides a readable Flush check. Functionally equivalent to the optimal solution, just more verbose.
|