1
#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?

  • Sorting the `cnt` array will lose track of the original indices of elements, so you need to somehow keep track of that. Also, subtracting `'a'` from a letter is not guaranteed to give a value between `0` and `25` for all character sets (it will work for ASCII or compatible character sets, but not others). – Peter Jun 24 '18 at 03:43

3 Answers3

4
#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.

Phillip Ngan
  • 12,647
  • 5
  • 61
  • 70
2

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.

Retired Ninja
  • 4,343
  • 3
  • 21
  • 35
  • `std::sort(cnt.begin(), cnt.end(), [](const LetterFreq& lhs, const LetterFreq& rhs) { return lhs.freq > rhs.freq; });` I have never seen this feature before, Is it c++ 14 based? – Antar Gebna Jun 24 '18 at 04:31
  • It's a lambda function. You need C++11 for it. https://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11 I debated using that or overloading `operator>` in the class and figured either way might be confusing. The lambda can do much more than that simple one, but for sorting it tends to save a lot of boilerplate code for the operator or a functior and the code is right where you can see it. – Retired Ninja Jun 24 '18 at 04:37
0

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.