Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

299. Bulls and Cows - Leetcode Solution

Problem Description

The "Bulls and Cows" problem is a classic code-breaking game. You are given two strings of equal length: secret and guess. Both strings consist of digits only. Your task is to determine:

  • Bulls: The number of digits that are in the correct position in both secret and guess.
  • Cows: The number of digits that are present in both secret and guess, but in different positions.

Each digit from secret and guess can only be counted once as either a bull or a cow. Digits cannot be reused for multiple matches.

The final answer should be returned as a string in the format "xAyB", where x is the number of bulls and y is the number of cows.

Constraints:

  • Both secret and guess have the same length.
  • Each digit in secret and guess is in the range '0' to '9'.
  • Do not reuse digits for multiple matches.

Thought Process

At first glance, this problem looks like a simple comparison between two strings. The challenge, however, is accurately counting bulls and cows without double-counting any digit.

The brute-force approach would involve checking every digit in guess against every digit in secret, but this would be inefficient and error-prone, especially with repeated digits.

To optimize, we can:

  • First, identify and count all bulls by comparing corresponding positions in both strings.
  • Then, for the remaining unmatched digits, count cows by tracking the frequency of each digit in secret and guess.
By separating the process into these two passes, we avoid double-counting and ensure that each digit is only matched once.

Solution Approach

Let's break down the solution step by step:

  1. Initialize Counters:
    • Set bulls = 0 and cows = 0.
    • Create two arrays or hash maps to count the unmatched digits in secret and guess.
  2. First Pass – Find Bulls:
    • Loop through each index i in secret and guess.
    • If secret[i] == guess[i], increment bulls. These are exact matches.
    • If not, increment the count for secret[i] and guess[i] in their respective counters.
  3. Second Pass – Find Cows:
    • For each digit (0-9), the number of cows is the minimum of its count in secret and guess (from the unmatched digits).
    • Sum these minimums to get the total number of cows.
  4. Return the Result:
    • Format the result as "{bulls}A{cows}B".

We use arrays (or hash maps) for digit counts because they allow O(1) lookup and update, which is efficient given the digit range.

Example Walkthrough

Example: secret = "1807", guess = "7810"

  1. First Pass (Find Bulls):
    • Index 0: 1 vs 7 — not a bull; increment counts.
    • Index 1: 8 vs 8 — bull! (bulls = 1).
    • Index 2: 0 vs 1 — not a bull; increment counts.
    • Index 3: 7 vs 0 — not a bull; increment counts.

    After this pass: bulls = 1.

    Unmatched counts:

    • secret: 1 (once), 0 (once), 7 (once)
    • guess: 7 (once), 1 (once), 0 (once)

  2. Second Pass (Find Cows):
    • For digit 1: appears once in both — add 1 cow.
    • For digit 0: appears once in both — add 1 cow.
    • For digit 7: appears once in both — add 1 cow.
    • Total cows = 3.
  3. Final Answer: "1A3B"

Time and Space Complexity

Brute-force Approach:

  • Time: O(n^2), as each digit in guess could be compared with every digit in secret.
  • Space: O(1), ignoring input size.
Optimized Approach (Two-pass):
  • Time: O(n), since we make two linear passes over the strings and O(1) work per digit.
  • Space: O(1), since digit counters are of fixed size (10 for digits 0-9).

The optimized approach is efficient and scalable, as it uses constant space and runs in linear time.

Summary

The Bulls and Cows problem challenges us to carefully count exact and partial matches between two digit strings, without reusing any digit. By separating the process into two passes—first for bulls, then for cows—and using frequency counters, we create a solution that is both simple and efficient. This approach highlights the power of breaking a problem into manageable steps and using the right data structure for the task.

Code Implementation

class Solution:
    def getHint(self, secret: str, guess: str) -> str:
        bulls = 0
        cows = 0
        from collections import Counter
        secret_count = [0] * 10
        guess_count = [0] * 10

        for s, g in zip(secret, guess):
            if s == g:
                bulls += 1
            else:
                secret_count[int(s)] += 1
                guess_count[int(g)] += 1

        for i in range(10):
            cows += min(secret_count[i], guess_count[i])

        return f"{bulls}A{cows}B"
      
class Solution {
public:
    string getHint(string secret, string guess) {
        int bulls = 0, cows = 0;
        int secret_count[10] = {0}, guess_count[10] = {0};
        for (int i = 0; i < secret.size(); ++i) {
            if (secret[i] == guess[i]) {
                ++bulls;
            } else {
                ++secret_count[secret[i] - '0'];
                ++guess_count[guess[i] - '0'];
            }
        }
        for (int i = 0; i < 10; ++i) {
            cows += min(secret_count[i], guess_count[i]);
        }
        return to_string(bulls) + "A" + to_string(cows) + "B";
    }
};
      
class Solution {
    public String getHint(String secret, String guess) {
        int bulls = 0, cows = 0;
        int[] secretCount = new int[10];
        int[] guessCount = new int[10];
        for (int i = 0; i < secret.length(); i++) {
            if (secret.charAt(i) == guess.charAt(i)) {
                bulls++;
            } else {
                secretCount[secret.charAt(i) - '0']++;
                guessCount[guess.charAt(i) - '0']++;
            }
        }
        for (int i = 0; i < 10; i++) {
            cows += Math.min(secretCount[i], guessCount[i]);
        }
        return bulls + "A" + cows + "B";
    }
}
      
var getHint = function(secret, guess) {
    let bulls = 0, cows = 0;
    let secretCount = Array(10).fill(0);
    let guessCount = Array(10).fill(0);
    for (let i = 0; i < secret.length; i++) {
        if (secret[i] === guess[i]) {
            bulls++;
        } else {
            secretCount[+secret[i]]++;
            guessCount[+guess[i]]++;
        }
    }
    for (let i = 0; i < 10; i++) {
        cows += Math.min(secretCount[i], guessCount[i]);
    }
    return `${bulls}A${cows}B`;
};