Antar Gebna
Antar Gebna

Reputation: 13

How to print frequency of each letter in a string in descending order c++?

#include <iostream>
#include <string>
using namespace std;
int main () {
    int cnt[26] {};
    char alpha[26];
        string s = "abcdefffggghiii";
        for (int i = 0; i < s.length(); i++) {
                cnt[s[i] - 'a']++;
        }

    for (int i = 'a'; i <= 'z'; i++) {
        alpha[i - 'a'] = i;
    }
    for (int i = 0; i < 26; i++) {
        if (cnt[i]) {
            cout << alpha[i] << " " << cnt[i] << endl;
        }
    }

    return 0;
}

I wanted to print the frequencies of each letter in the string in descending order. I've thought to sort the cnt array and print from 25 to 0 but it will only print the frequencies with wrong letter. How can I fix it to print for example i 3 and so on in descending order?

Upvotes: 1

Views: 1323

Answers (3)

Phillip Ngan
Phillip Ngan

Reputation: 16106

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int main() {

  // Create result container    
  auto x = vector<pair<char, int>>();

  std::string s = "abcdefffggghiii";
  for (auto& l : s) {    
    // Find the item that corresponds to letter
    auto pLetter =
        find_if(x.begin(), x.end(), [&l](pair<char, int> &arg) {
          return arg.first == l;
        });

    if (pLetter != x.end())
      pLetter->second++;   // If item corresponding to letter is found, increment count
    else {
      x.push_back(make_pair(l, 1)); // Otherwise, create a new result entry
    }
  }

  // Sort results by count in descending order
  std::sort(x.begin(), x.end(),
            [](auto &left, auto &right) { return left.second > right.second; });

  for (auto i = x.begin(); i != x.end(); ++i)
    std::cout << i->first << ':' << i->second << '\n';
}

Produces

f:3
g:3
i:3
a:1
b:1
c:1
d:1
e:1
h:1

You can run it here. This uses C++14 lambdas for the find_if and sort predicates. This solution is very similar to @Retired Ninja's, except that the result vector contains items only for those letters that have non-zero counts. This means that it is extendable to wstrings without the need for a large result vector.

Upvotes: 4

Retired Ninja
Retired Ninja

Reputation: 4925

Here's how I might do it. You just need to keep the letter and the count together.

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

struct LetterFreq
{
    char letter;
    int freq;
};

int main()
{
    std::vector<LetterFreq> cnt(26);
    for (size_t i = 0; i < cnt.size(); ++i)
    {
        cnt[i].freq = 0; 
        cnt[i].letter = static_cast<char>(i) + 'a';
    }
    std::string s = "abcdefffggghiii";
    for (auto& l : s)
    {
        cnt[l - 'a'].freq++;
    }
    std::sort(cnt.begin(), cnt.end(), [](const LetterFreq& lhs, const LetterFreq& rhs) 
    { 
        return lhs.freq > rhs.freq; 
    });
    for (auto& item : cnt)
    {
        if (item.freq == 0)
        {
            break;
        }
        std::cout << item.letter << " : " << item.freq << "\n";
    }
    return 0;
}

This is simple if all you have it lowercase ASCII letters. For more complicated input you can use the same idea of the letter and count in a struct, but you'd either want to increase the size of the vector to 256 to keep track of all possibilities, or use something like an unordered map to only store used symbols and then copy them out into a container you can sort to display them. You could also use parallel arrays and while sorting swap the letter positions at the same time you're swapping the counts. There are many ways to handle this.

Upvotes: 2

arealhumanbean
arealhumanbean

Reputation: 82

You could use pairs, but it looks like you're doing this with more basic types. In that case you might have to use nested loops. Keep finding the highest frequency character, print it, and then set its frequency to -1 to indicate that you've processed it already.

Upvotes: 0

Related Questions