2347. Best Poker Hand
Problem Description
You are given 5 playing cards represented by two arrays:
ranks
: an integer array whereranks[i]
is the rank (number) of the i-th cardsuits
: a character array wheresuits[i]
is the suit of the i-th card
Your task is to determine the best poker hand you can make from these 5 cards. The possible poker hands, ranked from best to worst, are:
- "Flush": All five cards have the same suit
- "Three of a Kind": Three cards have the same rank
- "Pair": Two cards have the same rank
- "High Card": No special pattern (any single card)
You need to return a string representing the best possible hand type. The return values are case-sensitive, so make sure to return the exact strings as shown above.
For example:
- If all cards have the same suit (like all hearts), return
"Flush"
- If three cards have rank 7, return
"Three of a Kind"
- If two cards have rank 5 but no three cards match, return
"Pair"
- If no special patterns exist, return
"High Card"
The algorithm checks for hands in order of strength - first checking for a Flush, then Three of a Kind, then Pair, and finally defaulting to High Card if none of the other patterns are found.
Intuition
Since we need to find the best possible poker hand, we should check for hands in order from best to worst. This way, as soon as we find a match, we can immediately return that hand type without checking the remaining possibilities.
For a Flush, all 5 cards must have the same suit. The simplest way to check this is to see if all suits are identical. We can either put all suits in a set and check if the set size is 1, or compare adjacent suits to ensure they're all the same.
For hands based on rank (Three of a Kind and Pair), we need to count how many times each rank appears. A frequency count naturally comes to mind - we can use a Counter
or hash table to track how many cards of each rank we have.
Once we have the frequency counts:
- If any rank appears 3 or more times, we have Three of a Kind
- If any rank appears exactly 2 times, we have a Pair
- Otherwise, we have High Card
The key insight is that we don't need to track complex combinations or positions - we just need to:
- Check if all suits match (for Flush)
- Count the frequency of each rank (for Three of a Kind and Pair)
- Return the first matching pattern we find, following the hierarchy of hand strength
This greedy approach works because the hand types are mutually exclusive in their ranking - if we have a Flush, it doesn't matter if we also have a Pair, since Flush is better.
Solution Approach
The solution follows a straightforward checking approach, evaluating each poker hand type from best to worst:
Step 1: Check for Flush
First, we check if all cards have the same suit. The code uses pairwise(suits)
to create pairs of adjacent elements and checks if all adjacent suits are equal:
if all(a == b for a, b in pairwise(suits)):
return 'Flush'
This is equivalent to checking if len(set(suits)) == 1
(commented out in the code), which verifies all suits are identical.
Step 2: Count Rank Frequencies
Next, we use Python's Counter
to count how many times each rank appears:
cnt = Counter(ranks)
This creates a dictionary where keys are ranks and values are their frequencies.
Step 3: Check for Three of a Kind
We iterate through the frequency values to check if any rank appears 3 or more times:
if any(v >= 3 for v in cnt.values()):
return 'Three of a Kind'
Step 4: Check for Pair
If no Three of a Kind exists, we check if any rank appears exactly 2 times:
if any(v == 2 for v in cnt.values()):
return 'Pair'
Step 5: Default to High Card
If none of the above conditions are met, we return the lowest-ranking hand:
return 'High Card'
The time complexity is O(n)
where n
is the number of cards (5 in this case), as we traverse the arrays a constant number of times. The space complexity is also O(n)
for the Counter dictionary, though with only 5 cards, both are effectively O(1)
.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through an example with the following 5 cards:
- ranks = [2, 3, 2, 2, 5]
- suits = ['a', 'b', 'a', 'c', 'd']
Step 1: Check for Flush
We examine if all cards have the same suit by comparing adjacent suits:
- Compare suits[0] with suits[1]: 'a' vs 'b' → not equal
- Since not all adjacent suits match, this is not a Flush
- Continue to next check
Step 2: Count Rank Frequencies
Create a frequency counter for the ranks:
Counter({2: 3, 3: 1, 5: 1})
This tells us:
- Rank 2 appears 3 times
- Rank 3 appears 1 time
- Rank 5 appears 1 time
Step 3: Check for Three of a Kind
Examine the frequency values: [3, 1, 1]
- Is any value ≥ 3? Yes! The value 3 is ≥ 3
- We found Three of a Kind (three cards with rank 2)
- Return "Three of a Kind"
The algorithm stops here and returns "Three of a Kind" without checking for Pair or High Card, since we found a match with a higher-ranking hand.
Let's trace through another example where we'd check further:
- ranks = [7, 10, 7, 3, 4]
- suits = ['a', 'b', 'c', 'd', 'a']
Step 1: Check for Flush → suits are not all the same ('a', 'b', 'c', 'd', 'a')
Step 2: Count Rank Frequencies → Counter({7: 2, 10: 1, 3: 1, 4: 1})
Step 3: Check for Three of a Kind → No value ≥ 3 in [2, 1, 1, 1]
Step 4: Check for Pair → Yes! Value 2 exists (two cards with rank 7)
- Return "Pair"
This demonstrates how the algorithm efficiently finds the best hand by checking in order of strength.
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def bestHand(self, ranks: List[int], suits: List[str]) -> str:
6 # Check for Flush: all cards have the same suit
7 if len(set(suits)) == 1:
8 return 'Flush'
9
10 # Count frequency of each rank
11 rank_count = Counter(ranks)
12
13 # Check for Three of a Kind: at least 3 cards with same rank
14 if any(count >= 3 for count in rank_count.values()):
15 return 'Three of a Kind'
16
17 # Check for Pair: at least 2 cards with same rank
18 if any(count == 2 for count in rank_count.values()):
19 return 'Pair'
20
21 # If none of the above conditions are met, it's a High Card
22 return 'High Card'
23
1class Solution {
2 public String bestHand(int[] ranks, char[] suits) {
3 // Check if all cards have the same suit (Flush)
4 boolean isFlush = true;
5 for (int i = 1; i < 5 && isFlush; i++) {
6 // If any suit differs from the previous one, it's not a flush
7 isFlush = (suits[i] == suits[i - 1]);
8 }
9
10 // Return "Flush" if all suits are the same
11 if (isFlush) {
12 return "Flush";
13 }
14
15 // Count frequency of each rank (1-13 possible ranks)
16 int[] rankCount = new int[14];
17 boolean hasPair = false;
18
19 // Iterate through all ranks to count occurrences
20 for (int rank : ranks) {
21 // Increment count for current rank
22 rankCount[rank]++;
23
24 // Check if we have three of a kind
25 if (rankCount[rank] == 3) {
26 return "Three of a Kind";
27 }
28
29 // Track if we have at least one pair
30 hasPair = hasPair || (rankCount[rank] == 2);
31 }
32
33 // Return "Pair" if we found a pair, otherwise "High Card"
34 return hasPair ? "Pair" : "High Card";
35 }
36}
37
1class Solution {
2public:
3 string bestHand(vector<int>& ranks, vector<char>& suits) {
4 // Check if all cards have the same suit (Flush)
5 bool isFlush = true;
6 for (int i = 1; i < 5 && isFlush; ++i) {
7 isFlush = (suits[i] == suits[i - 1]);
8 }
9
10 // If all suits are the same, we have a Flush
11 if (isFlush) {
12 return "Flush";
13 }
14
15 // Count frequency of each rank (1-13 possible ranks)
16 int rankCount[14] = {0};
17 bool hasPair = false;
18
19 // Iterate through all ranks to count occurrences
20 for (int& rank : ranks) {
21 // Increment count for current rank
22 rankCount[rank]++;
23
24 // Check if we have three of a kind
25 if (rankCount[rank] == 3) {
26 return "Three of a Kind";
27 }
28
29 // Mark if we found at least one pair
30 if (rankCount[rank] == 2) {
31 hasPair = true;
32 }
33 }
34
35 // Return "Pair" if we found a pair, otherwise "High Card"
36 return hasPair ? "Pair" : "High Card";
37 }
38};
39
1/**
2 * Determines the best poker hand from given ranks and suits
3 * @param ranks - Array of card ranks (1-13)
4 * @param suits - Array of card suits ('a', 'b', 'c', 'd')
5 * @returns The best possible hand type as a string
6 */
7function bestHand(ranks: number[], suits: string[]): string {
8 // Check if all cards have the same suit (Flush)
9 if (suits.every(suit => suit === suits[0])) {
10 return 'Flush';
11 }
12
13 // Initialize frequency counter for ranks (index 0-13)
14 const rankFrequency: number[] = new Array(14).fill(0);
15 let hasPair: boolean = false;
16
17 // Count frequency of each rank and check for combinations
18 for (const rank of ranks) {
19 // Increment the frequency counter for current rank
20 rankFrequency[rank]++;
21
22 // Check if we have three of a kind
23 if (rankFrequency[rank] === 3) {
24 return 'Three of a Kind';
25 }
26
27 // Track if we have at least one pair
28 hasPair = hasPair || rankFrequency[rank] === 2;
29 }
30
31 // Return Pair if we found at least one pair
32 if (hasPair) {
33 return 'Pair';
34 }
35
36 // Default to High Card if no other combinations found
37 return 'High Card';
38}
39
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the arrays ranks
and suits
(both have the same length of 5 in this problem).
- The
pairwise(suits)
operation withall()
iterates through the suits array once, takingO(n)
time. - The
Counter(ranks)
operation iterates through the ranks array once to count frequencies, takingO(n)
time. - The
any(v >= 3 for v in cnt.values())
iterates through at mostn
values in the counter, takingO(n)
time. - The
any(v == 2 for v in cnt.values())
similarly takesO(n)
time.
Since these operations are performed sequentially, the overall time complexity is O(n)
.
The space complexity is O(n)
:
- The
pairwise()
function creates an iterator that doesn't store all pairs at once, usingO(1)
space. - The
Counter(ranks)
creates a dictionary that stores at mostn
unique rank values and their counts, usingO(n)
space in the worst case.
Therefore, the overall space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Order of Checking Hand Types
One of the most common mistakes is checking for poker hands in the wrong order. Since we want to return the best possible hand, we must check from strongest to weakest.
Incorrect approach:
# Wrong: Checking Pair before Three of a Kind
rank_count = Counter(ranks)
if any(count == 2 for count in rank_count.values()):
return 'Pair' # This would return Pair even if there's Three of a Kind!
if any(count >= 3 for count in rank_count.values()):
return 'Three of a Kind'
Why it's wrong: If you have three cards of the same rank (e.g., three 7s), the count would be 3, which also satisfies the condition count >= 2
. The code would incorrectly return "Pair" instead of "Three of a Kind".
Solution: Always check hands in order of strength (Flush → Three of a Kind → Pair → High Card).
2. Using Wrong Comparison for Three of a Kind Check
Another pitfall is using count == 3
instead of count >= 3
when checking for Three of a Kind.
Incorrect approach:
# Wrong: Using exact equality instead of >=
if any(count == 3 for count in rank_count.values()):
return 'Three of a Kind'
Why it's wrong: While with exactly 5 cards you can't have more than 3 of the same rank while also having a pair, using >=
is more robust and follows the standard poker definition. If the problem were extended to more cards, using ==
would miss cases with 4 of a kind.
Solution: Use count >= 3
to properly identify Three of a Kind.
3. Case Sensitivity in Return Strings
The problem explicitly states that return values are case-sensitive.
Incorrect approach:
return 'flush' # Wrong case return 'three of a kind' # Wrong case and spacing return 'PAIR' # Wrong case
Solution: Use the exact strings specified: 'Flush'
, 'Three of a Kind'
, 'Pair'
, 'High Card'
.
4. Forgetting to Import Required Modules
The solution uses Counter
from collections, which must be imported.
Incorrect approach:
def bestHand(self, ranks: List[int], suits: List[str]) -> str:
rank_count = Counter(ranks) # NameError if Counter not imported
Solution: Always include necessary imports at the top of your solution:
from collections import Counter
Which data structure is used in a depth first search?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!