Skip to main content
GameDev.net gamedev.net
🔒 Locked

String: How to find the most common character occurrence.

Started by stitchs_login Mar 13, 2015 at 5:04 PM 11 replies 10k views
Original Post
stitchs_login
stitchs_login

Hi all,

I've been presented with a small challenge:

1) Make a function that counts the number of each individual character in a string. Report back to the user in the form the character that appears the most.

So the function would take a string input; i.e. "rabbit" and output the character that occurs the most in said string; i.e. <'b', 2>.

I have coded up a function (to my understanding of Big O notation is that it is Linear O(N)).


public static string GetMostFrequentCharacter(string input)
        {
            string result = "The String is empty.";

            if (IsStringValid(input))
            {
                int INDEX_CONVERTER = 32;

                int[] characterCounts = new int[127 - INDEX_CONVERTER];

                char characterToConvert;

                // first loop counts the occurrence of each character in the string
                for (int i = 0; i < input.Length; i++)
                {
                    characterToConvert = input[i];

                    characterCounts[(int)characterToConvert - INDEX_CONVERTER]++;
                }

                int highestCount = 0, arrayPosition = -1;

                // second loop finds which character appeared the most, only the last
                // highest count to appear in the list will count.
                for (int i = 0; i < characterCounts.Length; i++)
                {
                    // check to see if the current index value is higher
                    // than the previous higher count
                    if (characterCounts[i] >= highestCount)
                    {
                        // get the value at the current index
                        highestCount = characterCounts[i];
                        arrayPosition = i;
                    }
                }

                if (arrayPosition < 0)
                {
                    result = "There was an error in processing the string.";
                }
                else
                {
                    // finally, convert the arrayPosition into a suitable char representation
                    char characterToOutput = (char)(arrayPosition + INDEX_CONVERTER);
                    // format the output string
                    result = string.Format("<'{0}', {1}>", characterToOutput, highestCount);
                }
            }

            return result;
        }

I'm happy with the way most of it works. My only problem is that, say I have 2 characters that appear an equal number of times. My current method only takes the last highest value, in order of UNICODE value. Say we have the string "foof". The output for said function would be <'o', 2>, it does not present the last letter to appear, only the last in order of UNICODE.

I don't want to create another storage for characters that appear equal number of times (I already have 2 arrays). I have looked on the internet, but the only responses I am finding is if someone knows the character they are looking for, before they use the function; they ask "How many times does 'a' appear?"

Any help or feedback would be greatly appreciated.

Stitchs.

desdemian
desdemian

(...)

The output for said function would be <'o', 2>, it does not present the last letter to appear, only the last in order of UNICODE.

Is that what you are actually looking for?

If it is, here's what i would do...

Everytime you do:


characterCounts[(int)characterToConvert - INDEX_CONVERTER]++;

check if that newly increased value is the new highest... and if it is, update your values to display.

That way the printed character is the last one on the string and you also get rid of that second loop.

TheComet
TheComet

I was super confused as to what language you were using. "string" is small so I thought C++, but then I saw you're using "public static string" which smells like java. Then I noticed your function name is capital camel case and your braces aren't Java-like, which could make it C++ again, but most string and array operations are Java again. :S

Anyway, here's my go at solving the problem:

[EDIT] Code removed, turns out it was C# and I thought it was Java.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Bacterius
Bacterius

I was super confused as to what language you were using. "string" is small so I thought C++, but then I saw you're using "public static string" which smells like java. Then I noticed your function name is capital camel case and your braces aren't Java-like, which could make it C++ again, but most string and array operations are Java again. :S

It's C#, given that stitchs has been asking about a C# linked list implementation review in a previous topic.. that and the string.Format() call is a dead giveaway tongue.png

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”
TheComet
TheComet




It's C#, given that stitchs has been asking about a C# linked list implementation review in a previous topic.. that and the string.Format() call is a dead giveaway

Oh. In that case, ignore my post.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Nypyren
Nypyren
The code makes a bad assumption: It assumes the characters in the string are all in the range 32..127.

But chars in C# can be in the range 0..65535. Tabs, newlines, carriage returns are all very common characters will all cause your code to throw an IndexOutOfRangeException.

If your "IsStringValid" method is scanning the string for characters outside that range, then it's fine, but would also waste another loop over the string.
stitchs_login
stitchs_login

I completely agree Nypyren. This is a self-imposed limitation that I am doing to test with that particular set of Unicode characters. I wanted to refine the method first, and then expand it to include a bigger character set.

Stitchs.

lwm
lwm

Unless there are predetermined performance constraints, I would always go for a simple solution first:


private static string GetAnyMostFrequentCharacter(string input)
{
    if(!input.Any())
        throw new ArgumentOutOfRangeException("input");
    var group = input.GroupBy(c => c).OrderByDescending(g => g.Count()).First();
    return string.Format("<{0}, {1}>", group.Key, group.Count());
}

Or to get all most frequent characters:


private static string GetAllMostFrequentCharacters(string input)
{
    if (!input.Any())
        throw new ArgumentOutOfRangeException("input");
    var groups = input.GroupBy(c => c).OrderByDescending(g => g.Count());
    var firstLargestGroup = groups.First();
    var allLargestGroups = groups.Where(g => g.Count() == firstLargestGroup.Count());
    var stringBuilder = new StringBuilder();
    foreach (var group in allLargestGroups)
    {
        stringBuilder.AppendLine(string.Format("<{0}, {1}>", group.Key, group.Count()));
    }
    return stringBuilder.ToString();
}
current project: Roa
WozNZ
WozNZ

Unless there are predetermined performance constraints, I would always go for a simple solution first:


private static string GetAnyMostFrequentCharacter(string input)
{
    if(!input.Any())
        throw new ArgumentOutOfRangeException("input");
    var group = input.GroupBy(c => c).OrderByDescending(g => g.Count()).First();
    return string.Format("<{0}, {1}>", group.Key, group.Count());
}

Or to get all most frequent characters:


private static string GetAllMostFrequentCharacters(string input)
{
    if (!input.Any())
        throw new ArgumentOutOfRangeException("input");
    var groups = input.GroupBy(c => c).OrderByDescending(g => g.Count());
    var firstLargestGroup = groups.First();
    var allLargestGroups = groups.Where(g => g.Count() == firstLargestGroup.Count());
    var stringBuilder = new StringBuilder();
    foreach (var group in allLargestGroups)
    {
        stringBuilder.AppendLine(string.Format("<{0}, {1}>", group.Key, group.Count()));
    }
    return stringBuilder.ToString();
}

This

For OP.. Linq is your friend, learn to use it and your code complexity will wash away smile.png

ferrous
ferrous

It looked like an interview type question to me. I think I've even had it asked during an interview. The linq answer is nice for low complexity, but it's not the 'fastest', which I'm guessing the interviewer will want.

I'm not sure why you have two loops. You could keep track of the highest count and index in the first loop, then you wouldn't need to loop through again. (and as someone else pointed out, you're using an array to hold counts, you might want to try a dictionary instead.

And lastly, StringBuilder is more for when you're editing strings, string.concat is better for what you're doing there, which is just concatenating.

http://www.codeproject.com/Articles/14936/StringBuilder-vs-String-Fast-String-Operations-wit

WozNZ
WozNZ

It looked like an interview type question to me. I think I've even had it asked during an interview. The linq answer is nice for low complexity, but it's not the 'fastest', which I'm guessing the interviewer will want.

Depends on the job but during an interview I would be more interested in good grasp of a language and Linq is one of the more complex parts of the language as the lazy evaluation can be used to reduce work if you understand how it really flows. Simple on the skin but you need solid grounding to use it well.

I would say that unless you know absolute speed is what they are after simplicity of code wins every time as easier to change and maintain over long periods :)

ferrous
ferrous

Yeah, my first pass at most interview question answers is, "Well, I'm going to do the simple thing first" and then do the most straightforward / easy answer. Then they almost always go, "Okay, but make it faster". But It's still a good idea to show them you know Linq, and/or that simple answers are usually the way to go. (over-optimization is a thing)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.