0

I'm trying to find a way to display all the characters in a string and the number of times they occur.

This is what I have so far:

//Any unused includes are part of the default code

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
#include <string>

using namespace std;

int main()
{
    string st = "";

    cout << "Input a sentence: " << endl;
    getline(cin, st);
    int index = 0;
    int index2 = 0;
    int counters[26] = {0};
    for(int i = 0; i < st.length(); i++)
    {
        int counter = 0;
        index = st.find(st[i],0);
        for(int j = 0; j < st.length(); j++)
        {
            index2 = st.find(st[j]);
            if(index == index2)
            {
                counter++;
            }
        }
        cout << st[i] << ": " << counter << endl;
    }
    //cout << st[i] <<": " << counters[st[i] - 'a'] << endl;
    return 0;
}

and I return this:

Input a sentence:

hello

h: 1

e: 1

l: 2

l: 2

o: 1

so I kind of have something but I can't figure out how to make the letters not repeat more than once. I know that I need to store them in an array but it's out of my ken.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Rob
  • 43
  • 6
  • 1
    Not the best dupe target but it show how to do this: https://stackoverflow.com/questions/8010761/c-counting-instances-histogram-using-stdmap – NathanOliver Oct 12 '18 at 21:34
  • Consider iterating through `counters` and printing out the corresponding letter only if the count is greater than zero. You'll lose the ordering of the letters in the input, but I do not know if maintaining the ordering is a requirement. – user4581301 Oct 12 '18 at 21:41
  • have a `std::map` to keep the count for each letter – pm100 Oct 12 '18 at 21:46
  • @pm100: In this case, there's little real point. A map is basically imitating a sparse array, but in this case the dense array is typically going to be 104 bytes, so making it sparse doesn't save enough to notice, and will often backfire and use extra space. You typically have a tree with per-node balance information as well. So, even for the example shown (only 4 unique characters) it's a map may easily use more memory and time than a simple array. – Jerry Coffin Oct 12 '18 at 21:48
  • Well composed question, by the way. Minimal code and the problem statement, the output and the desired output are all present. Wish we saw more like this. – user4581301 Oct 12 '18 at 21:55
  • @JerryCoffin but its a great intro to real life c++ programming as opposed to fixed size arrays – pm100 Oct 12 '18 at 21:55
  • @pm100: Fair enough! :-) – Jerry Coffin Oct 12 '18 at 21:56
  • Your code is working (you just want it nicer). So you can take this to https://codereview.stackexchange.com to get some advice on better techniques. – Martin York Oct 12 '18 at 22:15

3 Answers3

1

You were very close, nice try! I liked the approach with the counters array, where every cell would represent the frequency of a letter in the given string.

So, just go and update this array, as this answer implies How to get character's position in alphabet in C language?, without the plus one they mention there, since you want the index of the letter in your array. In other words, for 'a', you need 0, 'b', you need 1 and so on.

Then, in the printing phase, just use the above link's suggestion in the reverse way. When you print the i-th non-zero element of counters, print the i-th element of the element, which will reveal the letter in question.

Putting all together, you get:

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
#include <string>

using namespace std;

int main()
{
    string st = "";

    cout << "Input a sentence: " << endl;
    getline(cin, st);
    int index = 0;
    int index2 = 0;
    int counters[26] = {0};
    for(size_t i = 0; i < st.length(); i++)
    {
        int counter = 0;
        index = st.find(st[i],0);
        for(size_t j = 0; j < st.length(); j++)
        {
            index2 = st.find(st[j]);
            if(index == index2)
            {
                counter++;
            }
        }
        counters[st[i] - 'a'] = counter; // update 'counters' array
    }
    for(int i = 0; i < 26; ++i)
        if(counters[i] != 0)            // print non-zero counters 
            cout << (char)(i + 'a') << ": " << counters[i] << endl;
    return 0;
}

Output:

e: 1
h: 1
l: 2
o: 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • If you are using `st[i] - 'a'` to get an index. Then we can use `i + 'a'` to get the letter back. I think the indirection through `alphabet` is confusing. – Martin York Oct 12 '18 at 22:13
  • @Roob I would note that this solution only works if the input `st` only contains lowercase letters. If there is anything else (space punctuation capitals) then the code will break. So you should explicitly check for these. – Martin York Oct 12 '18 at 22:14
  • @MartinYork correct, updated. As for the variety of inputs that it can handle, I agree, but I hope that my answer will be enough to get the OP un-stucked. ;) That was the reason I didn't use any containers from STL also, to just un-stuck OP, nothing more, nothing less! – gsamaras Oct 12 '18 at 22:18
  • And you are not testing if `getline(cin, st);` actually read anything. –  Oct 12 '18 at 22:35
0

I would do something like this:

#include <iostream>
#include <map>
#include <string>

int main()
{
    std::string st;

    std::cout << "Input a sentence: " << std::endl;
    std::getline(std::cin, st);

    std::map<char, int> m;

    for (const char c : st)
        ++m[c];

    for (const std::pair<char, int>& me : m)
        std::cout << me.first << ": " << me.second << std::endl;
}
  • Very good attempt, but try this for the second loop `for (const auto c : m) std::cout << c.first << ": " << c.second << std::endl;` – Richard Oct 12 '18 at 22:12
-1

lechuga2000 beat me to the post, but this is my suggestion:

#include <iostream>
#include <map>
#include <string>

int main()
{
    std::string input_sentence = "Now is the time for all good men to come to the aid of the party.";

/*
    std::cout << "Input a sentence: " << std::endl;
    std::getline(std::cin, input_sentence);
*/

    std::map<char, int> character_counts;

    for (const auto character : input_sentence)
        ++character_counts[character];

    for (const auto counted_character : character_counts)
        std::cout << counted_character.first << ": " << counted_character.second << '\n';

    return 0;
}

And here is the output:

 : 15
.: 1
N: 1
a: 3
c: 1
d: 2
e: 6
f: 2
g: 1
h: 3
i: 3
l: 2
m: 3
n: 1
o: 8
p: 1
r: 2
s: 1
t: 7
w: 1
y: 1
Richard
  • 8,920
  • 2
  • 18
  • 24