LeetCode 2347 - Best Poker Hand

The problem gives us exactly five playing cards. Each card is represented by two pieces of information: - ranks[i] represents the numerical rank of the card, from 1 to 13 - suits[i] represents the suit of the card, using characters from 'a' to 'd' We need to determine the…

LeetCode Problem 2347

Difficulty: 🟢 Easy
Topics: Array, Hash Table, Counting

Solution

Problem Understanding

The problem gives us exactly five playing cards. Each card is represented by two pieces of information:

  • ranks[i] represents the numerical rank of the card, from 1 to 13
  • suits[i] represents the suit of the card, using characters from 'a' to 'd'

We need to determine the strongest poker hand that can be formed from these five cards. The possible hands are ordered from strongest to weakest:

  1. "Flush", all five cards have the same suit
  2. "Three of a Kind", at least three cards share the same rank
  3. "Pair", at least two cards share the same rank
  4. "High Card", none of the above conditions apply

The key detail is that we only need to return the best possible category, not the actual cards that form the hand.

For example:

  • If all suits are identical, we immediately return "Flush", regardless of the ranks.
  • If we cannot form a flush, but some rank appears at least three times, we return "Three of a Kind".
  • If neither of the above exists but some rank appears twice, we return "Pair".
  • Otherwise, the answer is "High Card".

The constraints are very small:

  • There are always exactly 5 cards.
  • Rank values range from 1 to 13.
  • Suit values range from 'a' to 'd'.

Since the input size is fixed and tiny, performance is not a concern. However, we still want a clean and logically correct solution.

An important guarantee is that no two cards have both the same rank and the same suit, meaning duplicate physical cards cannot appear.

Several edge cases are important:

  • All suits identical, even if there are repeated ranks, should still return "Flush" because it has higher priority.
  • Four cards of the same rank also count as "Three of a Kind" because the problem only asks whether at least three equal ranks exist.
  • Multiple pairs should still return "Pair".
  • Completely distinct ranks and suits should return "High Card".

Approaches

Brute Force Approach

A brute force solution would explicitly test every possible hand category independently.

We could:

  1. Compare every suit against every other suit to check for a flush.
  2. Compare every pair of ranks to count equal values.
  3. Enumerate combinations of cards to see whether any three cards share the same rank.
  4. Enumerate pairs again to detect a pair.

Because there are only five cards, this approach is completely feasible. The total number of comparisons is tiny.

However, this method becomes messy because it repeatedly scans the same information. The logic is also harder to maintain because we manually inspect many combinations instead of using a structured counting strategy.

Optimal Approach

The key insight is that poker hand detection depends entirely on frequency counting.

  • A flush depends on whether all suits are identical.
  • Three of a kind depends on whether some rank appears at least three times.
  • A pair depends on whether some rank appears at least two times.

Instead of manually checking combinations, we can count how many times each rank appears using a hash map.

The algorithm becomes very simple:

  1. Check whether all suits match.
  2. Count rank frequencies.
  3. Find the maximum frequency among ranks.
  4. Return the correct hand type based on that maximum frequency.

This approach is cleaner, easier to reason about, and directly models the structure of the problem.

Approach Time Complexity Space Complexity Notes
Brute Force O(1) O(1) Manually compares combinations of cards
Optimal O(1) O(1) Uses frequency counting for ranks

Even though both are technically constant time because the input size is fixed at five cards, the optimal solution is more elegant and scalable in design.

Algorithm Walkthrough

  1. First, check whether all suits are the same.

We store the first suit and compare every other suit against it. If every suit matches, then the hand is a "Flush".

We do this first because "Flush" is the strongest possible hand in this problem. 2. Create a hash map to count rank frequencies.

The keys are card ranks, and the values are how many times each rank appears.

For example:

ranks = [4,4,2,4,4]

produces:

{
    4: 4,
    2: 1
}
  1. Compute the maximum rank frequency.

We iterate through the frequency map and track the largest count.

This tells us the strongest matching-rank pattern:

  • maximum frequency >= 3 means "Three of a Kind"
  • maximum frequency == 2 means "Pair"
  • otherwise "High Card"
  1. Return the best hand according to priority order.

Since flush is checked first, we only need rank analysis afterward.

Why it works

The algorithm works because the problem's hand definitions depend entirely on two properties:

  • Whether all suits are identical
  • The maximum number of equal ranks

The flush condition is independent of ranks, and the remaining hand categories are determined solely by the highest rank frequency. Since the algorithm correctly computes both properties and applies the hand ranking order from strongest to weakest, it always returns the correct best hand.

Python Solution

from typing import List
from collections import Counter

class Solution:
    def bestHand(self, ranks: List[int], suits: List[str]) -> str:
        # Check for Flush
        if all(suit == suits[0] for suit in suits):
            return "Flush"

        # Count rank frequencies
        rank_count = Counter(ranks)

        max_frequency = max(rank_count.values())

        if max_frequency >= 3:
            return "Three of a Kind"

        if max_frequency == 2:
            return "Pair"

        return "High Card"

The implementation starts by checking whether every suit matches the first suit. The all() function makes this concise and readable.

If a flush exists, we immediately return "Flush" because it outranks every other possible hand in this problem.

Next, we use Counter from Python's collections module to count how many times each rank appears. This automatically builds a frequency table.

We then compute the largest frequency value. If the maximum frequency is at least three, we return "Three of a Kind". If it is exactly two, we return "Pair". Otherwise, no repeated ranks exist, so the answer is "High Card".

Go Solution

func bestHand(ranks []int, suits []byte) string {
    // Check for Flush
    isFlush := true

    for i := 1; i < len(suits); i++ {
        if suits[i] != suits[0] {
            isFlush = false
            break
        }
    }

    if isFlush {
        return "Flush"
    }

    // Count rank frequencies
    rankCount := make(map[int]int)

    maxFrequency := 0

    for _, rank := range ranks {
        rankCount[rank]++

        if rankCount[rank] > maxFrequency {
            maxFrequency = rankCount[rank]
        }
    }

    if maxFrequency >= 3 {
        return "Three of a Kind"
    }

    if maxFrequency == 2 {
        return "Pair"
    }

    return "High Card"
}

The Go solution follows the same logic as the Python version.

Instead of Python's Counter, Go uses a map[int]int to track frequencies manually.

One small optimization is that we update maxFrequency while building the map, avoiding a second iteration through the frequency counts.

Since the input size is fixed and tiny, there are no concerns about overflow or memory usage.

Worked Examples

Example 1

ranks = [13,2,3,1,9]
suits = ["a","a","a","a","a"]

Step 1: Check Flush

Index Suit Matches First Suit?
0 a Yes
1 a Yes
2 a Yes
3 a Yes
4 a Yes

All suits match, so:

return "Flush"

Final answer:

"Flush"

Example 2

ranks = [4,4,2,4,4]
suits = ["d","a","a","b","c"]

Step 1: Check Flush

Not all suits are equal.

Continue to frequency counting.

Step 2: Build Rank Frequency Map

Rank Frequency After Processing
4 1
4 2
2 1
4 3
4 4

Final frequency map:

{
    4: 4,
    2: 1
}

Maximum frequency:

4

Since 4 >= 3:

return "Three of a Kind"

Final answer:

"Three of a Kind"

Example 3

ranks = [10,10,2,12,9]
suits = ["a","b","c","a","d"]

Step 1: Check Flush

Suits are not all equal.

Step 2: Build Rank Frequency Map

Rank Frequency
10 2
2 1
12 1
9 1

Maximum frequency:

2

Since the maximum frequency equals 2:

return "Pair"

Final answer:

"Pair"

Complexity Analysis

Measure Complexity Explanation
Time O(1) Only five cards are processed
Space O(1) Frequency map stores at most 13 ranks

The algorithm processes a fixed number of cards, exactly five, so the running time never grows with input size. The frequency map also has a strict upper bound because ranks only range from 1 to 13.

Technically, the solution could also be described as O(n) time and O(n) space for a generalized version with n cards, but under the given constraints it behaves as constant time and constant space.

Test Cases

from typing import List
from collections import Counter

class Solution:
    def bestHand(self, ranks: List[int], suits: List[str]) -> str:
        if all(suit == suits[0] for suit in suits):
            return "Flush"

        rank_count = Counter(ranks)
        max_frequency = max(rank_count.values())

        if max_frequency >= 3:
            return "Three of a Kind"

        if max_frequency == 2:
            return "Pair"

        return "High Card"

solution = Solution()

# Provided examples
assert solution.bestHand([13,2,3,1,9], ["a","a","a","a","a"]) == "Flush"  # all suits same
assert solution.bestHand([4,4,2,4,4], ["d","a","a","b","c"]) == "Three of a Kind"  # four equal ranks
assert solution.bestHand([10,10,2,12,9], ["a","b","c","a","d"]) == "Pair"  # one pair

# High card case
assert solution.bestHand([1,2,3,4,5], ["a","b","c","d","a"]) == "High Card"  # all unique

# Exactly three of a kind
assert solution.bestHand([7,7,7,2,3], ["a","b","c","d","a"]) == "Three of a Kind"  # exactly triple

# Multiple pairs
assert solution.bestHand([8,8,3,3,5], ["a","b","c","d","a"]) == "Pair"  # two pairs still count as Pair

# Flush with repeated ranks
assert solution.bestHand([9,9,9,9,2], ["c","c","c","c","c"]) == "Flush"  # flush has higher priority

# Minimum rank values
assert solution.bestHand([1,1,2,3,4], ["a","b","c","d","a"]) == "Pair"  # lowest rank duplicates

# Maximum rank values
assert solution.bestHand([13,13,13,12,11], ["a","b","c","d","a"]) == "Three of a Kind"  # highest rank triple
Test Why
All suits identical Validates Flush detection
Four equal ranks Ensures counts >= 3 return Three of a Kind
Single pair Validates Pair detection
All distinct cards Validates High Card
Exactly three equal ranks Confirms normal triple handling
Two separate pairs Ensures best result is still Pair
Flush with repeated ranks Confirms Flush priority
Minimum rank duplicates Tests lower boundary values
Maximum rank triple Tests upper boundary values

Edge Cases

One important edge case is when all five suits are identical while ranks also contain duplicates. A naive implementation might incorrectly prioritize repeated ranks and return "Three of a Kind" or "Pair". The correct answer must still be "Flush" because the problem explicitly defines it as the stronger hand. The implementation handles this by checking for a flush before any rank analysis.

Another important case is when four cards share the same rank. The problem does not include a separate "Four of a Kind" category. This means any frequency of three or greater must return "Three of a Kind". The implementation correctly uses >= 3 instead of == 3.

A third edge case occurs when there are two separate pairs, such as [8,8,3,3,5]. The problem does not define "Two Pair" as a valid category. The correct answer is simply "Pair". Since the algorithm only checks the maximum frequency, it naturally returns "Pair".

Another subtle case is when all cards are completely distinct in both rank and suit. In this scenario, the frequency map contains only counts of 1, and the suits are not identical. The algorithm correctly falls through to "High Card" after all stronger conditions fail.