user2550864
user2550864

Reputation:

Function to count all characters in a string - C++

I want to write a functioin in C++, which counts all characters in a string.# I have a string called input, in which the user of the program can enter a sentence, the letters that are important I stored in a string alphabet like this:

string alphabet {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};

and a vector that is used to store the frequency of occurrence of the letters, e.g. A is located on place 0, B on place 0, and so on.

vector<long> letterCount (26);

I have written the function like I think it should work, and it seems that it is able to figure out the occurences of the characters but after that this figure is multiplied by the place of the letter in the alphabet. Here is the function:

long countLetters(int& p) {
  for(int i = 0; i < alphabet.size(); ++i) {
      for(long j = 0; j < count(input.begin(), input.end(), alphabet.at(i)) {
          countLetters.at(i)++;
      }
  }
return letterCount.at(p);
}

For example, if the input is "HELLO" the programs puts out:

E : 5
H : 8
L : 24
O : 15

So you see, for example the letter 'L' is contained two times in the string, but the result for 'L' is 24, because 'L' is at place 12 in the alphabet.

Please help, if you realize what my problem is.

EDIT: I've found a way that works, at least partially:

long countLetters(int& p) {
   for(size_t i = 0; i < input.length(); ++i) {
      for(size_t j = 0; j < alphabet.length(); ++j) {
        letterCount.at(j) = count(input.begin(), input.end(), alphabet.at(j));
      }
   }
   return letterCount.at(p);
 }

But when entering two or more words the function only figures out the letter-occurences in the first word. How do I analyze more words?

EDIT: before I had cin >> input but getline(cin, input); is right.

Upvotes: 4

Views: 35602

Answers (10)

Akshat Gupta
Akshat Gupta

Reputation: 13

The string can be taken as an argument from the user.

cin >> inputString;

unordered_map<char, int> characterMap;

for (char c : inputString){
    characterMap[c]++;
}
for (std::pair<char, int> characterCount : characterMap) { // Alternatively use 'auto' as type
    cout << characterCount.first << " count: " << characterCount.second << endl;
}

Upvotes: 1

Scuodder
Scuodder

Reputation: 1

#include<iostream>
#include <conio.h>
using namespace std;

int main (){
char str[50];
cin.getline(str,50);
int arr[1234]={0};

///extraction of every character 
int i=0;
while(str[i]!='\0'){

arr[str[i]-' ']++;    /* converting characters into integer type implicitly 
                       and storing freq of the ASCII characters at that 
                        position of the array.' ' space char is just a 
                        reference point... */ 


i++;
}



///show character freq
for (i=0;i<256;i++) {
if (arr[i]!=0)
cout <<"letter "<<char(i+' ')<<"  is present "<<arr[i]<<" times "<<endl;

}

return 0;

}

/* the arr array contains freq of all the characters and symbols occuring in 
a string after ' '(space) character ..so beware of entering the characters 
that are before ' ' on the standard ASCII table and your program should run 
fine..if you want to do so just replace the ' ' everywhere with the first 
character of the ASCII table.....*/

Upvotes: 0

Sam Mokari
Sam Mokari

Reputation: 471

Test this macro

#define FOR_ALL(cont , block)\
for (const auto &itr : cont)\
    block;

And this part of Code

map<char, int> countLetters;
FOR_ALL(str, countLetters[itr]++);

And for printing the result

for (const auto &element : m)
    cout << element.first << ' ' << element.second<<endl;

Upvotes: 0

user5944916
user5944916

Reputation: 11

char arr[] = {"aaabbaccdaadac"}; 
    map<char,int> mymap;
    for(int i= 0 ;i<strlen(arr);i++)
    {            
        mymap.insert(pair<char,int>(arr[i],0));
          auto it = mymap.find(arr[i]);
          ++it->second;
          mymap.insert(pair<char,int>(arr[i],it->second));

     }

    map<char, int> ::iterator mapit;
    for(mapit = mymap.begin(); mapit != mymap.end() ; mapit++)
    {
        cout<<mapit->first<< "   occurence   ==  " <<mapit->second<<endl;
    }

Upvotes: 1

Edison Chang
Edison Chang

Reputation: 335

This is my version to solve the problem and print the result in descending order.

void printNumofLetterinString(std::string sentence){
    int frequencyArray[26];         //FrequencyArray is used to store the frequency
    for(int i=0;i<26;i++){          //of the letters and Initialize 
        frequencyArray[i] = 0;      //frequencyArray to all zero.
    }
    int ascii;
    for(int i=0;i<sentence.length();i++){
        if(!isalpha(sentence[i])){
            continue;
        }
        ascii = tolower(sentence[i]) - 'a';   //Convert A-Za-z to number between 0-25.
        frequencyArray[ascii]++;
    }
    for(int i=0;i<26;i++){              //Find the biggest number in frequencyArray     
        int max = frequencyArray[0];    //print it, then set it to zero  
        int index = 0;                  //and find the next biggest number.
        for(int j=0;j<26;j++){
            if(frequencyArray[j] > max){
                max = frequencyArray[j];
                index = j;
            }
        }
        if(max == 0){
            break;
        }
        char c = index + 'a';
        std::cout<<c<<" "<<max<<std::endl;
        frequencyArray[index] = 0;
    }
}

The result looks like the following

input caaabb
output a 3
       b 2
       c 1

Upvotes: 1

Branko Dimitrijevic
Branko Dimitrijevic

Reputation: 52107

You could do something like this:

std::string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string input = "MISSISSIPPI yukon.";

// First insert the characters that we want to count.
std::unordered_map<char, size_t> map;
for (auto ch : alphabet)
    map[ch] = 0;

// Then count ONLY the inserted characters.
for (auto ch : input) {
    auto it = map.find(ch);
    if (it != map.end())
        ++it->second;
}

for (auto pair : map)
    if (pair.second > 0)
        std::cout << '\'' << pair.first << "\'\t" << pair.second << std::endl;

This prints...

'I'     4
'M'     1
'P'     2
'S'     4

...since we count only the characters from alphabet.

Replace std::unordered_map with std::map if you wish to guarantee ordered results (they are ordered in the example above by accident).

Upvotes: 0

user2107435
user2107435

Reputation:

you can also do this :

 char   *x = "cmnasdkASFSAFASDisdajkhasdfjqwedz" ; // work UPPER , lower
 static int  c[26] ;

 int main ( void ) {

 while ( *x )   {
     int ndx= *x - (islower(*x) ? 'a' : 'A') ;
     c[ ndx] += isalpha(*x++) ? 1 : 0 ;
 }
 for ( int i =0;i<26;i++)   printf ( "\n%d", c[i] );  
}

Upvotes: 0

KillianDS
KillianDS

Reputation: 17176

I would do this in two steps like this:

#include <unordered_map>
#include <algorithm>
#include <string>
#include <iostream>

int main()
{
    std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
    std::string input = "hello world";
    std::unordered_map<char, unsigned int> counts;
    std::unordered_map<char, unsigned int> counts2;
    std::for_each(std::begin(input), std::end(input), [&counts](char c) {
        counts[c]++;
    });
    std::for_each(std::begin(alphabet), std::end(alphabet), [&counts, &counts2] (char c) {
        const auto& it = counts.find(c);
        if( it != counts.end()) counts2.insert(*it);        
    });
    for(auto& kv: counts2)
    {
        std::cout << kv.first << ": " << kv.second << "\n";
    }
    return 0;
}

As access to an unordered map should be in the order of O(1) this will result in a complexity of O(N+M), with N being the length of the input string and M the length of the output string. You might be able to improve the copying between counts and counts2, or eliminating the extra map altogether, I was a bit in a hurry when writing this up ;). You can also get back to putting the output in a vector, but I'll leave that as an excercise.

Another variant would be to store your alphabet in a set and do an if(alphabetset.count(c)) in the first loop and do not do the second loop. This would have complexity O(N*log(M)) which can also be good enough and the code is a bit simpler:

#include <unordered_map>
#include <algorithm>
#include <string>
#include <iostream>
#include <set>

int main()
{
    std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
    std::set<char> alphabetset{std::begin(alphabet), std::end(alphabet)};
    std::string input = "hello world";
    std::unordered_map<char, unsigned int> counts;
    std::for_each(std::begin(input), std::end(input), [&counts, &alphabetset](char c) {
        if(alphabetset.count(c)) counts[c]++;
    });
    for(auto& kv: counts)
    {
        std::cout << kv.first << ": " << kv.second << "\n";
    }
    return 0;
}

Of course if your input set has some mathematical properties (like being an exact range), you can use a solution like Tom van der Woerdt's, because this will be O(N) and you can't get faster than that.

Upvotes: 2

syam
syam

Reputation: 15069

As mentioned by @KillianDS in a comment, if you want a generic solution (ie. an "alphabet" that can vary) the easiest way is probably to count the occurrences of every possible character, then filter that depending on your actual alphabet:

// count every possible character
std::array<size_t, (1 << (8 * sizeof(char)))> countChars;
countChars.fill(0);
for (auto i = input.begin(); i != input.end(); ++i)
    countChars[*i]++;
// extract only the ones you're interested in
std::vector<size_t> countLetters;
for (auto i = alphabet.begin(); i != alphabet.end(); ++i)
    countLetters.push_back(countChars[*i]);

Note: when counting items, better use size_t than long or int.

Upvotes: 0

Tom van der Woerdt
Tom van der Woerdt

Reputation: 29965

You're doing some kind of weird double loop. Instead, iterate over the string in a single loop and count it in the right group :

for (int i = 0; i < input.length(); i++) {
    char c = input[i];
    if (c < 'A' || c > 'Z') continue;
    countLetters[c-'A'] += 1;
}

Upvotes: 4

Related Questions