My current program creates a huffman tree filled with nodes of ascii characters that are being read from a text file along with the amount of time they appear in the text file (frequency). In addition, it outputs unique codes for each character read based on frequency, this is done using my traverse function.
My problem: I have this string array that can hold codes for all 256 ascii values in my huffman function that by default is set to an empty string for each element. I have been trying to update my array by passing a parameter to my traverse function but it gives me an error.
Code E0413 - "No suitable conversion from std::string to char exists"
Parts of my code below along with some explanation of what the variables in my traverse function are:
'character' is the char that has been found in the text file
'frequency' gives the number of times a character is read in the text file
'traversecode' is the huffman code being generated by the traverse function
I have also commented out lines in my traverse function where I get the error.
struct node {
int frequency;
char character;
const node *child0;
const node *child1;
node(unsigned char c = 0, int i = -1) {
character = c;
frequency = i;
child0 = 0;
child1 = 0;
}
node(const node* c0, const node *c1) {
character = 0;
frequency = c0->frequency + c1->frequency;
child0 = c0;
child1 = c1;
}
bool operator<(const node &a) const {
return frequency > a.frequency;
}
void traverse(string codearray[256], string traversecode = "") const {
if (child0) {
child0->traverse(traversecode + '0'); // one line throwing the error
child1->traverse(traversecode + '1'); // second line that throws me the error
}
else {
codearray[int(character)] = traversecode;
cout << " " << character << " ";
cout << frequency;
cout << " " << traversecode << endl;
}
}
};
huffman function (function that contains array I would like to get updated)
void huffman(string code[256], const unsigned long long frequency[256]) {
priority_queue < node > q;
for (unsigned i = 0; i < 256; i++) {
if (frequency[i] == 0) {
code[i] = "";
}
}
for (int i = 0; i < 256; i++)
if (frequency[i])
q.push(node(i, frequency[i]));
while (q.size() > 1) {
node *child0 = new node(q.top());
q.pop();
node *child1 = new node(q.top());
q.pop();
q.push(node(child0, child1));
}
cout << "CHAR FREQUENCY HUFFMAN CODE" << endl;
q.top().traverse(code);
}
When you make the recursive call to traverse, you need to provide both parameters.
child0->traverse(codearray, traversecode + '0');
You're currently trying to pass what should be the second parameter as the first.
One other possible issue is that your code assumes that char is unsigned. If a char is signed, the access to codearray[int(character)] will access outside the bounds of codearray if the character is "negative" (or in the upper half of the ASCII table when using unsigned characters).
Related
In my Huffman Algorithm project, so far I have generated the codes for each character of the input file. I have also stored the characters and their corresponding codes in an unordered map. Now, I want to read our input string, and print the corresponding codes of each character in the output file. However, printing the codes in string format will not compress the file. I want to convert my string code to bits format. I know that we need to use a byte buffer, but I do not know how I will apply this concept to my code. Any help will be very much appreciated.
#include<iostream>
#include<string>
#include<queue>
#include<vector>
#include<bitset>
#include<fstream>
#include<unordered_map>
#include<map>
using namespace std;
struct node
{
char c; //character in the string
int f; //Frequency of character in the string
node* next;
node* left, * right; //left and right child of binary tree respectively
node()
{
f = 0;
left = NULL;
right = NULL;
c = NULL;
next = NULL;
}
};
struct compare {
public:
bool operator()(node* a, node* b) // overloading both operators
{
return a->f > b->f; //To maintain the order of min heap priority queue
}
};
class Huffman
{
string filename; //The name of the file we want to encode
string text; //The text that will be encoded
priority_queue<node*, vector<node*>, compare> pq; //Priority queue that will contian characters of our string and their frequency
string encoded;
unordered_map <char, string> um;
public:
Huffman()
{
text = "";
encoded = "";
}
void FileRead()
{
cout << "Enter the name of the file you want to encode:";
cin >> filename;
fstream readfile(filename, fstream::in);
getline(readfile, text, '\0');
cout << text << endl;
readfile.close();
}
//Function which will calculate the frequency of characters in the string entered by the user
void CharacterFrequency()
{
for (int i = 0; i < text.length(); i++)
{
int sum = 0;
for (int j = 0; j < text.length(); j++)
{
if (j < i and text[i] == text[j])
{
break;
}
if (text[i] == text[j])
{
sum++;
}
}
if (sum != 0)
{
PriorityQueue(text[i], sum);
}
}
}
// This will push our characters and their frequencies into our STL min heap priority queue
void PriorityQueue(char ch, int freq)
{
node* n=new node; //pointer of type node is created
n->c = ch; //Pointer stores character
n->f = freq; //Pointer stores frequency of the character
pq.push(n); //The node is pushed into the priority queue
}
//Will display the whole priority queue. All of the elements will be popped from it as a result.
void PriorityQueueDisplay()
{
while (!pq.empty())
{
cout << (pq.top())->c<<" "<<(pq.top())->f << endl;
pq.pop();
}
}
//This function will create our Huffman Tree from a priority queue
void HuffmanTree()
{
node* n1, * n2; //The nodes that will be popped each time from the priority queue
//This loop will continue to pop out two nodes from the priority queue until only one nodes is left
//in the priority queue
while (pq.size()!=1)
{
n1 = pq.top();
pq.pop();
n2 = pq.top();
pq.pop();
node* z = new node; //Creation of new node of Huffman tree
z->left = n1;
z->right = n2;
z->f = (n1->f) + (n2->f); //Storing sum of the two popped nodes in Huffman tree node
z->c = '&'; //Assigning the new node a character that is not used in formal speech
pq.push(z); //Pushing the node into the priority queue again
}
node* root = pq.top(); //Making the last node the root node
EncodeAndPrintCodes(root,encoded); //Passing the root node and a string that will encode each character of our inputted string
}
//This function will recursively search for a character in the string, and will print it's corresponding code.
//It will do this for all our characters
void EncodeAndPrintCodes(node* root,string en)
{
if (root == NULL)
{
return ;
}
if (root->c != '&')
{
//cout << root->c << ":" << en;
StoreinMap(root->c, en);
}
EncodeAndPrintCodes(root->left, en + "0");
EncodeAndPrintCodes(root->right, en + "1");
}
//Will convert our code in string to bitstream and then store it in a text file
void CompressedFile(char ch, string code)
{
ofstream compressed;
compressed.open("CompressedFile.txt", ios::app | ios::out);
}
void StoreinMap(char ch, string code)
{
um.emplace(pair<char, string>(ch,code));
}
/*void DisplayEncoded()
{
cout << encoded;
}*/
//Displays the size of the priority queue
void DisplaySize()
{
cout<<pq.size();
}
};
int main()
{
Huffman obj;
obj.FileRead();
obj.CharacterFrequency();
//obj.PriorityQueueDisplay();
obj.HuffmanTree();
//obj.DisplaySize();
//obj.DisplayEncoded();
//obj.CompressedFile();
return 0;
}
Copied from this answer, this is a way to write bits to a file of bytes. You have a bit buffer consisting of:
unsigned long bitBuffer = 0;
int bitcount = 0;
To add the bits bits in value to the buffer:
bitBuffer |= value << bitCount;
bitcount += bits;
To write and remove available bytes:
while (bitCount >= 8) {
writeByte(bitBuffer & 0xff);
bitBuffer >>>= 8;
bitCount -= 8;
}
At the end, you need to write any bits remaining in the bit buffer. When decoding, you need to take care to not interpret any filler bits in the last byte as data that it is not. For that, you'll need an end marker in your data.
Some side comments. Somehow you turned the O(n) calculation of character frequencies into an O(n^2) calculation! You should think about that some more. Don't define special character values. You should be able to compress any sequence of bytes. Your use of getline() stops reading the input if it gets to a zero byte. Use rdbuf(). Your use of & as an indicator of a node because you think the character is "not used in formal speech" is wrong. (It is commonly used in writing.) If there is an ampersand in the input, your program will crash, trying to access an uninitialized pointer. Use left as your indicator of whether this is a node or a leaf by setting it to nullptr if it's a leaf.
I have been assigned as a school project to check the the frequency of occurrence of 26 lowercase letters, and then they are encoded by Hoffman code. In this assignment, the basic requirement is (1)to read the given text file and open it on the terminal and (2)to output the number of occurrence of lowercase letters and their corresponding encoded Hoffman code.
I was able to accomplish the (2) task but whenever I am trying to output the text on the terminal, my code was not able to count the occurrence of the lowercase letters and output their respective Huffman code.
terminal showing only the text content
output without the inclusion of code for reading the text on terminal
Here is my code for the assignment:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <bits/stdc++.h>
int ch;
FILE *file;
using namespace std;
struct Node
{
// data members of each node of Huffman tree - alphabet and its frequency
char data;
unsigned freq;
// pointers to left and right childs
Node *left, *right;
// constructor to initialize the data members of the Huffman tree node
Node(char d, unsigned f)
{
data = d;
freq = f;
left = NULL;
right = NULL;
}
};
struct compare
{
// overloading () to compare which of left,right childs have higher frequency
bool operator()(Node* l, Node* r)
{
return (l->freq > r->freq);
}
};
// recursive function to display huffman codes
void display(struct Node* root, string str)
{
// if tree is empty return
if (!root)
return;
if (root->data != '$')
cout << root->data << ": " << str << "\n";
// recursively call left, right childs
display(root->left, str + "0");
display(root->right, str + "1");
}
// build huffman tree
void createHuffmanTree(char data[], int freq[], int size)
{
struct Node *left, *right, *top;
// Create a min heap using STL
priority_queue<Node*, vector<Node*>, compare> minHeap;
// push alphabets, frequency into minheap as leaf nodes
for (int i = 0; i < size; ++i)
minHeap.push(new Node(data[i], freq[i]));
// repeat until heap size becomes one
while (minHeap.size() != 1)
{
// Extract two least frequent items from minheap
left = minHeap.top();
minHeap.pop();
right = minHeap.top();
minHeap.pop();
// Create new internal node with two least frequent items as child nodes
// frequency of internal node is equal to sum of frequency of child nodes
top = new Node('$', left->freq + right->freq);
top->left = left;
top->right = right;
// push newly created internal node into minheap
minHeap.push(top);
}
// building Huffman tree is done
// display Huffman codes for alphabets
cout << "The Huffman codes for lower case characters: " << endl;
display(minHeap.top(), "");
}
// reading text file
void readTxt() {
FILE * txt;
char ch;
if (txt != NULL)
{
// open file to read english article
txt = fopen("D:\\data3.txt", "r");
do
{
ch = fgetc(txt);
putchar(ch);
} while(ch != EOF);
//fclose(txt);
} else {
cout << "There is no such .txt file in the system." << endl;
exit(0);
}
}
int main()
{
int charCount = 0;
int freq[26] = {0};
readTxt();
// reads english article from file and counts frequency of each lowercase alphabet
while (1)
{
ch = fgetc(file);
if (ch == EOF)
break;
if (ch >= 'a' && ch <= 'z')
{
freq[ch - 'a']++;
// count total number of lowercase alphabets in file
charCount++;
}
}
fclose(file);
// display total number of characters in file
cout << "Number of lowercase alphabets in file: " << charCount << endl;
char arr[] = { 'a','b','c','d','e','f',
'g','h','i','j','k','l',
'm','n','o','p','q','r',
's','t','u','v','w','x',
'y','z' };
// display frequency of lowercase characters
cout << "Frequency of lowercase characters: " << endl;
int freqCount = 0;
for(int i = 0; i < 26; i++)
{
if(freq[i]!=0)
{
cout << arr[i] << " : " << freq[i] << endl;
// count number of lowercase alphabets with frequency greater than zero
freqCount++;
}
}
char arr1[freqCount];
int k = 0, freq1[freqCount];
// copy lowercase alphabets with frequency greater than zero into arr1, freq1
for(int i = 0; i < 26; i++)
{
if(freq[i]!=0)
{
arr1[k] = arr[i];
freq1[k++] = freq[i];
}
}
// call method to create Huffman tree
createHuffmanTree(arr1, freq1, freqCount);
return 1;
}
It will be a great help if you guide me through my mistake.
TIA
I have a bit of trouble with programming sometimes, but I'm generally alright and always have the issue of understanding the concept perfectly, but I hit a brick wall when I attempt to implement an actual program to perform an operation.
I have an assignment to work on, in which I must take an input string, read it character by character into a stack (using a linked list) and then pop that result out of the stack, store it to a new string and then compare the strings to determine if that particular input string is or isn't a palindrome.
The only issue so far that I seem to have ran into is (hopefully) at the very end of the program. When I attempt to pop each character off the stack and store them in a string individually, I get an issue where Visual Studio tells me: "error C2664: 'Stack::pop' : cannot convert parameter 1 from 'unsigned int' to 'char &'"
void Stack::pop(char &input_string) {
StackNode* temp;
if (isEmpty()) {
cout << "The stack is empty." << endl;
}
else {
input_string = top->value;
temp = top->next;
delete top;
top = temp;
}
}
int main () {
Stack stringStack;
string input_string;
string reverse_input;
cout << "Input the desired string to determine if it is a palindrome or not. No spaces please." << endl;
cin >> input_string;
for (unsigned int i=0; i < input_string.length(); i++) {
stringStack.push(input_string[i]);
}
while (!stringStack.isEmpty()) {
for (unsigned int j=0; j < input_string.length(); j++) {
stringStack.pop(j) = reverse_input[j];
}
}
if (reverse_input == input_string) {
cout << "Your input is a palindrome!" << endl;
}
else {
cout << "Your input was not a palindrome, try again!" << endl;
}
system ("PAUSE");
}
I realize that it is telling me that I cannot pass j into the pop function to pop out the values because the pop function I have declared is expecting a char value.
Could this be remedied by changing the input to the pop function to an integer, and then using the pop function to return the char value instead?
I am excluding all other function of the cpp file other than the pop function and the main execution function, let me know if you need to see another part for some reason.
Thanks in advance for the help. It is much appreciated.
In this part:
while (!stringStack.isEmpty()) {
for (unsigned int j=0; j < input_string.length(); j++) {
stringStack.pop(j) = reverse_input[j];
}
}
Assuming that the goal is to take the element at the top of the stack with pop and add it at the end of the string reverse_input so that the resulting reverse_input would be the reverse of input_string, there is one too many loops.
for( unsigned int j = 0; !stringStack.isEmpty() ; j++ ){
//Code Here
}
Or:
while( !stringStack.isEmpty() ){
//Code Here
}
Also stringStack.pop(j) = reverse_input[j]; isn't actually assigning anything, as pop is a void function.
One alternative is the following:
Modify the pop so that it returns the char element at the top.
char Stack::pop() {
StackNode* temp;
char mychar = '\0';
if (isEmpty()) {
cout << "The stack is empty." << endl;
}
else {
mychar = top->value;
temp = top->next;
delete top;
top = temp;
}
return mychar;
}
Then:
reverse_input = ""; //To make sure it is empty
while( !stringStack.isEmpty() ){
reverse_input += stringStack.pop();
}
So I'm writing a program that takes in a string of characters in a command line statement, and breaks up the word into two or three pieces (2 for even, first half and second half, 3 for odd, first "half", middle letter, and second "half), and reverses the characters of the first and second halves and re concatenates the characters into a single string htat is outputted. It gets a little uglier than that, as I have to use a deque and use push and pop functions to move around the characters. So I have a few problems I don't really understand. First off, the ABottom integer for some reason is blowing up to outrageously large values, which makes no sense as it is supposed to stay fixed at 0. Secondly, when I pop from A, I get an empty string, and when I pop from B, it alternates every other character from the deque. But the loops in the .h file that put the characters in the deque seems to be working exactly as I anticipated. Any suggestions about the ABottom, or why the pops aren't working?
File 1:
// Kevin Shaffer TwoStackKAS.h
#include<iostream>
#include<string>
#include<vector>
#ifndef TWOSTACKKAS_H_
#define TWOSTACKKAS_H_
using namespace std;
class TwoStacks {
char elements[];
int Abottom, Bbottom;
int AtopSpace, BtopSpace;
int totalSize;
public:
TwoStacks(int maxAdds) {
totalSize = 2*maxAdds +1;
char elements[totalSize];
const int Bbottom = totalSize-1; //bottom for both stacks!
const int Abottom = 0;
AtopSpace= 0;
BtopSpace = totalSize-1; //top for both stacks!
cout<<"Stack Size: "<<totalSize<<endl;
}
virtual bool empty() const {
return Abottom == AtopSpace && Bbottom==BtopSpace;
}
virtual bool full() const { return AtopSpace==BtopSpace;}
virtual int stackSize() {
cout<<Abottom<<" Abottom"<<endl;
return (AtopSpace - Abottom +Bbottom -BtopSpace);
}
virtual char popA() {
if (empty()){
cerr << "Attempting to pop Empty stack!"<< endl;
return ' '; //prepare EmptyQexceptiin
} else {
cout << elements[--AtopSpace] << " testpopA"<< endl;
return elements[--AtopSpace];
}
}
virtual char popB() {
if (empty()){ //later EmptyQException
cerr <<"Attempting to pop an empty stack!" << endl;
return ' ';
} else {
//cout <<elements->at(++BtopSpace) << endl;
cout << elements[++BtopSpace] << " test"<< endl;
return elements[++BtopSpace];
}
}
virtual void pushB(char newItem){
elements[BtopSpace--] = newItem;
}
virtual void pushA(char newItem){
elements[AtopSpace++] = newItem;
}
virtual string toString() const {
string out = "";
for (int i = 0 ; i<=Bbottom; i++) {
out += elements[i];}
return out;
}
};
#endif
And file 2:
/** Kevin Shaffer
* Given an input string, reverse each half of the string;
* pivot on the middle element if it exists.
* uses double ended stack in twoStackKAS.h*/
#include<string>
#include "TwoStackKAS.h"
#include<iostream>
#include<string>
using namespace std;
int main (int argc, char* argv[]){
if (argc<=1){return 0;}
string word = argv[1];
int length = word.size(); // gets length of word
int half = length/2;
TwoStacks* sd = new TwoStacks(length/2);
//cout<<sd->stackSize()<<endl;
for(int i = 0; i < length/2; i++){
sd->pushA(word[i]);
cout << word[i] << endl;
}
for(int i = length; i >= length/2; i--){ //Second half of word
sd->pushB(word[i] ); //has been pushed
cout << word[i] << endl; //in reverse order.
}
//cout << word << endl;
//Recombine word
if(length%2==1){ word = word[length/2];}
else{ word = "";}
cout<<sd->stackSize()<<endl;
string leftHalf; string rightHalf;
string myWord; //new word (shuffled)
for(int i=0; i< half; i++) {
leftHalf += sd->popA();
rightHalf += sd->popB();
}
//cout<<"Stack: "<<sd->toString()<<endl;
cout << rightHalf << endl;
cout << leftHalf << endl;
myWord = leftHalf + word + rightHalf;
cout<<myWord<<endl;
return 0;
}
ABottom is not growing... it is never initialized!
Look at your constructor. You're not assigning Abottom, but you're defining a new variable that masks the member variable. You do that multiple times.
And as VS2015 actually did not accept char elements[]; : "Incomplete type is not allowed", it's probably better it use std::string instead of char*
A better constructor would be something like.
class TwoStacks
{
private:
// totalSize has to be assigned before assigning dependent variables
int totalSize;
string elements;
int Abottom, Bbottom;
int AtopSpace, BtopSpace;
public:
TwoStacks(int maxAdds)
: totalSize(2 * maxAdds + 1)
, elements(totalSize, ' ')
, Abottom(0)
, Bbottom(totalSize - 1)
, AtopSpace(0)
, BtopSpace(totalSize - 1)
{
// preferably don't cout in a constructor
cout << "Stack Size: " << totalSize << endl;
}
I have string say "walk talk, can't won't Won't woN'T talk." I want to count the reapeated words and display.
Note: it is not case sensitive.
I have used delimeter
strtok(string, ",.;:\"!? -_\n\t*()##=+");
and saved it in
char *temp[100];
Now how can I check for repeatation of words? And display as below
3 won't
2 talk
1 can't
1 walk
it should display from highest repeat to lowest. And if the repeatation is same then display alphabetic order.
Sorry for my bad english.
Use a std::string to hold the result of the strtok(). Then create a std::map<string, int> to hold the count of the times the string (the key) has occurred.
You can populate the map with:
std::map<string, int> myMap;
myMap[tokenizedWord]++; //Increase count of word.
You can then cycle through the map content and print out wherever the integer value is greater than 2.
for (std::map<string, int>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
if (iter->second > 1)
std::cout << "Duplicated word: " << iter->first << " count = " << iter->second;
}
I'll let you figure out how to traverse it in order. You can put the values in a vector or something and use std::sort before printing or whatever else you like. Maps, unfortunately, are associative containers and you can't sort them as it breaks their internal ordering.
Background Info on std::map
A map is an associative array meaning that every key maps to a specific value, and keys are unique. You can actually create a multimap where keys are not unique, so that's why this is important.
Basically, since keys are unique, you can access or create an element just by using the key as the array index.
For example:
//Create a map and insert a couple things into it - prices of meat?
std::map<string, float> myMap;
myMap["Chicken"] = 4.99;
myMap["Turkey"] = 6.99;
//Retrieve the price of something using the key.
std::cout << "Chicken costs " << myMap["Chicken"] << std::end;
You can do standard insertion and location operations on a map too, but the associative array syntax is just simpler, so why bother? :)
PS: To fully answer your comment, just in case, the ++ at the end of myMap[tokenizedWord]++ is just saying to increment the value of the integer value stored for that key by 1. You could just as well do myMap[tokenizedWord] = myMap[tokenizedWord] + 1 OR you could also do myMap[tokenizedWord] += 1.
a complete implementation of your problem (Let me know if you want a sample code for sorting):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define ARRAY_ELEMS_COUNT(A) sizeof(A)/sizeof(*A)
typedef struct _word_t
{
char *word;
int occurr_count;
struct _word_t *next;
} word_t;
typedef struct _word_list_t
{
struct _word_t *head;
struct _word_t *tail;
int elems_count;
} word_list_t;
/* Creation of the words list */
word_list_t *make_list(void)
{
word_list_t *w_list = (word_list_t *)malloc(sizeof (struct _word_list_t));
if (w_list == NULL)
{
fprintf(stderr, "malloc faild --> %s\n", strerror(errno));
return NULL;
}
w_list->head = w_list->tail = NULL;
w_list->elems_count = 0;
return w_list;
}
int list_word_lookup(word_list_t *w_list, char *word)
{
word_t *temp_word = w_list->head;
while(temp_word)
{
if (strcmp(temp_word->word, word) == 0)
{
/* We got it before, increment the count */
temp_word->occurr_count++;
return 1;
}
else
{
temp_word = temp_word->next;
}
}
return 0;
}
/* Adding new words to the list of words if they are not present, otherwise increment their occurrence count */
/* TODO : Sort the list using Merge sort for performance */
int adding_to_list(word_list_t *w_list, char *word)
{
int return_status = 0;
char *tmp_word = (char *)malloc(sizeof(char)*(strlen(word) + 1));
word_t *new_word = (word_t *)malloc(sizeof(struct _word_t));
/* Empty list */
if (w_list->head == NULL)
{
strcpy(tmp_word, word);
new_word->word = tmp_word;
new_word->occurr_count = 1;
w_list->head = w_list->tail = new_word;
w_list->head->next = NULL;
w_list->elems_count++;
}
else
{
/* The list is not empty */
/* Checking if the word exist in the list */
return_status = list_word_lookup(w_list, word);
if (return_status == 1)
{
fprintf(stdout, "WE got this word before --> increment count\n");
}
else
{
strcpy(tmp_word, word);
new_word->word = tmp_word;
new_word->occurr_count = 1;
w_list->tail->next = new_word;
w_list->tail = new_word;
w_list->tail->next = NULL;
}
}
return 0;
}
void words_list_dump(word_list_t *w_list)
{
word_t *temp;
for (temp = w_list->head; temp; temp = temp->next) {
fprintf(stdout, "Word : %s -- Count = %d\n", temp->word, temp->occurr_count);
}
}
/* Destroying all words */
void free_words(word_list_t *w_list)
{
word_t *temp;
for (temp = w_list->head; temp; temp = temp->next) {
/* Freeing the word string */
free(temp->word);
/* Freeing the word */
free(temp);
}
w_list->head = NULL;
w_list->tail = NULL;
}
/* Destroying the words list */
void free_words_list(word_list_t *w_list)
{
if (!w_list)
{
return;
}
free_words(w_list);
free(w_list);
}
/* TODO : create a function that converts your input text to a char ** array, so you can pass it to adding_to_list */
/* For testing */
int main(int argc, char **argv)
{
const char *string[] = {"Hello", "World", "Stackoverflow", "C", "Hello", "C", "WORDS", "words", "List", "list", "Hello", "World", "Count"};
word_list_t *my_list = make_list();
int i;
for (i = 0; i < ARRAY_ELEMS_COUNT(string); i++)
adding_to_list(my_list, string[i]);
words_list_dump(my_list);
free_words_list(my_list);
return 0;
}
Here is an answer using strtok but without std::map. In one pass of string, every word in is checked against previous words and repeats are counted.
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <cstring>
using std::tolower;
int main()
{
char *strin;
string inputstr;
vector<string> svec;
vector<int> cvec;
char *pch;
int unique_word_count=0;
while(getline(cin,inputstr))
{
//token-ize the string
//First string
strin = &inputstr[0];
pch = std::strtok(strin," ,-");
bool unique_word_found = true;
//subsequent words
while (pch != NULL)
{
string word(pch);
for(string::size_type i=0; i < word.size(); i++)
word[i]=tolower(word[i]);
//first word
//just add to svec and no comparisons
if(unique_word_count==0)
{
svec.push_back(word);
cvec.push_back(1);
cvec[unique_word_count++]=1; //init count of first word
//next word
pch = std::strtok(NULL, " ,-");
unique_word_found = true; //reset flag
continue;
}
//start comparing with other words currently in string vector
//do not do this if only 1 word present
vector<string>::iterator iter=svec.begin();
while(iter < svec.end())
{
if(word == *iter)
{
//match found
cvec[iter-svec.begin()]++; //increment count of that word
unique_word_found = false;
}
iter++;
}
if(unique_word_found)
{
//add to unique word list and increment count
svec.push_back(word);
cvec.push_back(1);
cvec[unique_word_count++]=1;
}
//next word
pch = std::strtok(NULL, " ,-");
unique_word_found = true; //reset flag
}
}
cout << "Word" << " ---> " << "Occurences" << endl;
for(vector<string>::size_type i=0; i < svec.size(); i++)
{
cout << svec[i] << " ---> " << cvec[i] << endl;
}
return 0;
}
The general strategy can be as follows:
Sanitize the input (convert all characters to lower case, remove unwanted punctuation, etc.)
Walk through the input
Add each character to a string, finalizing when a space is encountered
Add the string to a key-value structure. The string is the key. If this is a new entry not already contained in the structure, set the value 1. Otherwise set it to the current value + 1 (so as to count the number of times encountered so far).
Repeat for each word
Walk through the key-value structure and print each entry.