Bulls and cows

Posted by everrover on September 14, 2021

#langoptimization
#craptalk
#counting
#optimization

New day new question🌄️. This question is present at link on Leetcode. The question is real easy, but there is one curious observation. And that observation made me want to pull out my hair.💢

Solution first. The number of bulls🐂 (digits present in both guess and secret, but in place) can be calculated using one traversal and counting equal numbers within the string. And the number of cows🐄 (digits present in both guess and secret, but in wrong place) can be calculated by storing the occurances of digits in both secret and guess, and then adding up minimum of both occurances for each digit.

Example is shown below, Secret : <u>1</u>5981<br/> Guess : <u>1</u>6815<br/>

Here striked values are uncommon digits. Underlined ones are bulls. Rest are cows.

It works in O(n) of time and memory complexity.

Here is the code,

java
class Solution {
    public String getHint(String secret, String guess) {
        int bulls = 0, cows = 0;
        final int n = secret.length();
        char []secretArr = secret.toCharArray();
        char []guessArr = guess.toCharArray();
        
        int []digitsS = new int[10];
        int []digitsG = new int[10];
        for(int i=0; i<n; i++){
            int s = secretArr[i]-'0';
            int g = guessArr[i]-'0';
            if(s == g){
                bulls++;
            }else{
                digitsS[s]++;
                digitsG[g]++;
            }
        }
        for(int i=0; i<10; i++){
            cows+=Integer.min(digitsS[i], digitsG[i]);
        }
        // Solution ONE
        // return bulls+"A"+cows+"B";
        // Solution TWO
        return new StringBuilder().append(bulls).append('A').append(cows).append('B').toString();
    }
}

Please note the last lines. While returning first variant uses direct concatenation and second uses StringBuilder.

The curious thing is that, I thought direct concatenation will help get results faster, since no StringBuilder object is being used. But that wasn't the case. On average 4ms of time was taken up by direct concatenation alone. Logic took 1ms on average. Whereas StringBuilder only took 0ms (rounded off, technically time taken is between 0-0.5ms) for the same concatenation.

There is one rule of thumb I follow 👍

Algorithm submitted with less than 95th percentile is an unoptimised algorithm.

So I always try to push it over 95😤. And this trivial change pushed it to <80th percentile🤨 to 100th percentile🙃️.

Hope you don't get stuck due to such trivial issues.

By the way I have developed a small guessing game, based on the same question🧑‍💻. Do check it out.

Cheers guys👋.