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 ith 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.