Palindromes in C++ - c++

I'm trying to make a program that uses stacks (w pop, push, etc.) to read in a text file with lots of sentences that are taken one at a time and outputs whether each line is a palindrome or not (words that are spelled the same forwards and backwards). I believe its very close to being a completed program, but it only returns false even when the string is a palindrome. I want it to return true when the string is in fact a palindrome.
EDIT: Tried a new method with three stacks instead. Still getting a false return from bool tf every time.
int main() {
Stack s(100); // Initialize two different stacks
Stack q(100);
Stack temp(100);
string line; // String to hold each individual line of the file
char letter;
char x; // For comparisons
char y;
// open the file
ifstream input;
input.open(READFILE);
// Check that it is open/readable
if (input.fail()) {
cout << endl << "Sorry, file not available, exiting program. Press enter";
cout << endl;
cin.get(); // Grab the enter
return 0;
}
while (getline(input, line)) { // Read the file line-by-line into "line"
cout << "The line: " << line << endl;
int length = line.length(); // Sets length equal to string length
for (int i =0; i<length; i++){ // Capitalizes string
line[i] = toupper(line[i]);
}
for (int i = 0; i < length; i++) { // Loop through for every letter in the line
if (line[i] == ' ' ) {
line.erase(i,1); // Takes spaces out of the line
length--;
}
if (ispunct(line[i])){
length--;
}
if (!ispunct(line[i])){ // Removes punctuation
letter = line[i]; // Push each letter onto the stack
s.push(letter);
}
}
for (int i = 0; i < length; i++) { // Popping half the letters off of the s stack
s.pop(letter); // and pushing them onto the q stack
q.push(letter);
temp.push(letter);
}
for (int i = 0; i < length; i++) {
temp.pop(letter);
s.push(letter);
}
bool tf = true; // Pop off the top of each stack and compare
while (!s.empty()) { // them to check for a palindrome
s.pop(x);
q.pop(y);
if (x == y);
else tf = false;
}
if (tf){
cout << "is a palindrome!" << endl;
}
if (!tf) {
cout << "is NOT a palindrome" << endl;
}
}
}

for (int i = 0; i < length/2; i++) // Popping half the letters off
//of the s stack
q.push(letter); // and pushing them onto the q
//stack
}
Here you're pushing the same letter over and over again.
Even if you rewrite as the comment states it will be wrong.
if you pop half of ABBA you have BA and AB and compare B=A
you need to rethink your strategy. Maybe push half of the string to s then loop backwards from length and push to q

Like someone else mentioned, even after fixing the for loop with the "q" stack, the general strategy is not correct. In fact you don't need two stacks. (or even one stack, but you can use a stack if desired.)
You do have the right idea in comparing the back half of the letters with the front half. In general, to find a palindrome you just need to see if the string is equal to the reversed string or that the first half is equal to the back half.
You can use a stack to store the string in reverse. All you need is the stack and the string. However, there is the extra problem here in that the lines of strings contain spaces and punctuation that you want to ignore. Using the erase() method reduces the length of the string as you go, so you need a temporary variable to rebuild the formatted string at the same time as the stack. EDIT: I saw your update to accounting for the reduced length; that's great -- it can save even the use of a temp variable to hold the formatted string so that the variable string line is all that is needed.
Here is another version of your while loop that uses one stack and a temp string variable. It uses half the formatted string to compare against the top of the stack (which represents the "back" of the string).
string cleanString;
//cout << "test3";
while (getline(input, line)) { // Read the file line-
//by-line into "line"
cout << "The line read was: " << line << endl;
int length = line.length(); // Sets length equal to
//string length
for (int i =0; i<length; i++) // Capitalizes string
line[i] = toupper(line[i]);
for (int i = 0; i < length; i++) // Loop through for //every letter in the line
if ( !(line[i] == ' ' || ispunct(line[i]))) { // Ignore space & punctuation
letter = line[i]; // Push each letter onto
s.push(letter); //the stack
cleanString.push_back(letter); //and to the "cleaned" string to compare with later
//cout << cleanString << endl; //test
}
length = cleanString.length();
bool tf = true;
for (int i = 0; i < length/2; i++) { // Pop off the top of stack
s.pop(x); // to compare with front of string
if ( cleanString[i] != x ) { //not a palindrome
tf = false;
break;
}
}
if (tf){
cout << "is a palindrome!" << endl;
}
if (!tf) {
cout << "is NOT a palindrome" << endl;
}
}
But it's simpler to skip the use of the stack altogether and instead just use the temp "cleaned" string, checking for a palindrome in a for loop with two counters: one for the the front and one for the back.
So after the capitalization:
// Instead of a stack, just build a string of chars to check for a palindrome
for (int i = 0; i < length; i++)
if ( !(line[i] == ' ' || ispunct(line[i]))) {
letter = line[i]; // Push each letter onto
cleanString.push_back(letter); // a temp string
}
length = cleanString.length(); //use length of formatted string
bool tf = true;
int front = 0; // first char of string
int back = length-1; // last char of string
for (; i < length/2; front++, back--)
if ( cleanString[front] != cleanString[back] ) { //not a palindrome
tf = false;
break;
}
Another option is to use the inbuilt reverse() function in the <algorithm> header file after building the temp string:
#include <algorithm> // reverse()
string cleanString;
string reversedCleanString;
//...
// Instead of a stack, just build a string of chars to check for a palindrome
for (int i = 0; i < length; i++)
if ( !(line[i] == ' ' || ispunct(line[i])))
cleanString.push_back(line[i]);
reversedCleanString = cleanString; // store copy of string to reverse
reverse(reversedCleanString.begin(), reversedCleanString.end() ); // reverse
bool tf = true;
if ( cleanString != reversedCleanString)
tf = false;
// ...
As moooeeep mentioned the comments, using std::string's reverse iterators simplifies this even further after the capitalization:
string cleanString;
//...
// Format line to test if palindrome
for (int i = 0; i < length; i++)
if ( !(line[i] == ' ' || ispunct(line[i])))
cleanString.push_back( line[i] );
bool tf = true;
if ( cleanString != string(cleanString.rbegin(), cleanString.rend() )
tf = false;
// ...
Also, like moooeeeep mentioned, encapsulating the different parts of the while loop into their own separate functions is a good idea to make not just debugging easier but also understanding the logical flow of the problem more intuitively.
For example the while loop could look like this:
while (getline(input, line)) { // Read the file line-by-line into "line"
//echo input
cout << "The line read was: " << line << endl;
// validate/format the line
extractChars( line ); //remove space/punctuation
capitalizeString( line ); // capitalize chars for uniformity
//check if formatted line is a palindrome and output result
if ( is_palindrome( line ) )
cout << "Line IS a palindrome << endl;
else
cout << "Line IS NOT a palindrome << endl;
}

Related

Converting this program from 1 function to a modular, multi-function program?

I am very new to C++ and have a program which I included below.
The program I am working on reads text from an input file and counts the number of words and number of occurrences of each letter in the text and then prints the results. My program is working fine but the problem is all code is written in the main function and I need to break it up into a couple more functions to make the program modular, but I am unsure of how to go about doing this.
I am sure this is pretty simple but I'm not sure where to start. I was thinking of implementing two void functions, one for reading / interpreting what is read from the data file and another that displays the results; and then call them both in the main function, but I'm not sure what to take as arguments for those functions.
int main()
{
// Declaring variables
char c; // char that will store letters of alphabet found in the data file
int count[26] = {0}; // array that will store the # of occurences of each letter
int words = 1; // int that will store the # of words
string s; // declaring string found in data file
// Opening input file stream
ifstream in;
in.open("word_data.txt");
// Reading text from the data file
getline(in, s);
//cout << s << endl;
// If input file fails to open, displays an error message
if (in.fail())
{
cout << "Input file did not open correctly" << endl;
}
// For loop for interpreting what is read from the data file
for (int i = 0; i < s.length(); i++) {
// Increment word count if new line or space is found
if (s[i] == ' ' || s[i] == '\n')
words++;
//If upper case letter is found, convert to lower case.
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] = (tolower(s[i]));
//If the letters are found, increment the counter for each letter.
if (s[i] >= 'a' && s[i] <= 'z')
count[s[i] - 97]++;
}
// Display the words count
cout << words << " words" << endl;
// Display the count of each letter
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
c = i + 97;
cout << count[i] << " " << c << endl;
}
}
// Always close opened files
in.close();
return 0;
}
I would rewrite it like:
class FileReader {
public:
FileReader() {
// Any init logic goes here...
}
~FileReader() {
// Always close opened files.
in.close();
}
void open(std::string &filePath) {
in.open(filePath);
}
std::string readLine() {
std::string s;
getline(in, s);
return s;
}
bool hasErrors() const { // remove const if you get compile-error here.
return in.fail();
}
private:
ifstream in;
};
class LetterCounter {
public:
void process(std::string &s) {
// For loop for interpreting what is read from the data file
for (int i = 0; i < s.length(); i++) {
// Increment word count if new line or space is found
if (s[i] == ' ' || s[i] == '\n')
words++;
//If upper case letter is found, convert to lower case.
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] = (tolower(s[i]));
//If the letters are found, increment the counter for each letter.
if (s[i] >= 'a' && s[i] <= 'z')
count[s[i] - 97]++;
}
}
void logResult() {
char c; // char that will store letters of alphabet found in the data file.
// Display the words count
cout << words << " words" << endl;
// Display the count of each letter
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
c = i + 97;
cout << count[i] << " " << c << endl;
}
}
}
private:
int count[26] = {0}; // array that will store the # of occurences of each letter
int words = 1; // int that will store the # of words
};
int main()
{
// Opening input file stream.
FileReader reader;
reader.open("word_data.txt");
// Reading text from the data file.
std::string s = reader.readLine();
// If input file fails to open, displays an error message
if (reader.hasErrors()) {
cout << "Input file did not open correctly" << endl;
return -1;
}
LetterCounter counter;
counter.process(s);
// Display word and letter count.
counter.logResult();
return 0;
}
Note that I did write without testing (excuse any mistake),
but this should give you a general idea how it should be.

Program Won't Continue to Run When Testing for Palindromes (C++)

I'm a beginner in coding with C++. I'm trying to make a program that completes a specific set of tasks:
opens a data file,
takes each line of the file,
processes each line by removing all whitespace and punctuation,
converts the string into all lowercase,
uses a recursive method to test if the string is a palindrome,
finds a position within the string where characters can be added to make it a palindrome if it isn't already,
then adds the characters needed to make it a palindrome in the position specified in (6).
I'm only allowed to use 4 user-defined functions with specific parameters. So far I've got about 80% of the program to work, but there's an error when it detects a non-palindrome. I'm hoping someone can find out why. Here's my code:
// Read file data, check for palindromes, and process strings to palindromes.
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
using namespace std;
string process(string);
bool is_palindrome(string);
int palindrome_fix_location(string);
string palindrome_addition(string, int);
int main()
{
ifstream inFile;
inFile.open("data");
string preString;
int palinLoc = 0;
while(getline(inFile, preString))
{
cout << "\nOriginal line: " << preString << endl;
cout << "Processed line: " << process(preString) << endl;
if (is_palindrome(preString) == true)
cout << "Line is palindrome." << endl;
else
cout << "Line is NOT a palindrome." << endl;
palindrome_fix_location(preString);
palindrome_addition(preString, palinLoc);
}
inFile.close();
return 0;
}
// Return a string that is lowercase with no punctuation or spacing.
string process(string preString)
{
string procString;
for (size_t i = 0; i < preString.length(); i++)
{
if (isalnum(preString[i]))
procString += tolower(preString[i]);
}
return procString;
}
// Uses a recursive method to determine if the processed string is a palindrome.
bool is_palindrome(string procString)
{
string temp = process(procString);
int length = temp.length();
string firstChar = temp.substr(0, 1);
string lastChar = temp.substr((length - 1), 1);
if (firstChar == lastChar)
{
temp = temp.substr((0 + 1), (length - 2));
if (temp.length() <= 1) // Base case.
return true;
return is_palindrome(temp); // Recursion.
}
else
return false;
}
// Return a location where text can be added to the non-palindrome to make it a palindrome.
int palindrome_fix_location(string procString)
{
string temp = process(procString);
if (is_palindrome(temp) == false)
{
int palinLoc;
int firstChar = 0, lastChar = temp.length() - 1;
while (firstChar < lastChar)
{
if (temp[firstChar] != temp[lastChar])
{
palinLoc = firstChar;
cout << "Characters to insert at location "
<< palinLoc << " are ";
return palinLoc;
}
}
}
return 0;
}
// Return the text that needs to be added at the "palinLoc" location.
string palindrome_addition(string procString, int palinLoc)
{
string temp = process(procString);
string addedChars;
string finalString;
if (is_palindrome(temp) == false)
{
int firstChar = 0, lastChar = temp.length() - 1;
while (firstChar < lastChar)
{
do {
addedChars += temp[lastChar];
} while (temp[firstChar] != temp[lastChar]);
firstChar++;
lastChar--;
}
finalString = temp.insert(palinLoc, addedChars);
cout << addedChars << endl;
cout << "Final word: " << finalString << endl;
return finalString;
}
else
return finalString;
}
And here's the output I get:
Original line: lappal
Processed line: lappal
Line is palindrome.
-
Original line: lapal
Processed line: lapal
Line is palindrome.
-
Original line: A man, a plan, a canal, Panama!
Processed line: amanaplanacanalpanama
Line is palindrome.
-
Original line: lap
Processed line: lap
Line is NOT a palindrome.
Right there when it says "Line is NOT a palindrome," it's supposed to follow up with something that looks like this:
Characters to insert at location 0 are pa
Final line: palap
It just stops at "Line is NOT a palindrome." Can anyone see where I went wrong with this?
Any help would be greatly appreciated.
your loop here (in palindrome addition) is bugged
do {
addedChars += temp[lastChar];
} while (temp[firstChar] != temp[lastChar]);
it never ends
so you should move either lastchar or firstchar change inside, like this for example
while (firstChar < lastChar)
{
do {
addedChars += temp[lastChar];
lastChar--;
} while (temp[firstChar] != temp[lastChar])
firstChar++;
}
some run here
Original line: lap
Processed line: lap
Line is NOT a palindrome.
start palindrom fix
Characters to insert at location 0 are
start palidrome addition
pa
Final word: palap
Original line: lapin
Processed line: lapin
Line is NOT a palindrome.
start palindrom fix
Characters to insert at location 0 are
start palidrome addition
nipa
Final word: nipalapin
Original line: lapal
Processed line: lapal
Line is palindrome.

Q: C++ - Reversing a string (sentence and word) using a class

For my code, I am trying to create a class with two functions that:
Display a cstring where each word is reversed
Display an entire cstring reversed
My two test sentences are "Hi There" and "To Be", so the output is:
erehT iH
eB oT
iH erehT
oT eB
Here is my code:
#include <iostream>
#include <cstring>
using namespace std;
class cStringType {
public:
char sentenceInput[80]; //Member variable
void reverse_sentence(); //Member function
void reverse_words(); //Member function
}; //Bottom of cStringType
int main()
{
cStringType sentence1, sentence2;
//Objects declared of cStringType
cout << "Please enter a sentence!\n" << endl;
cin.get(sentence1.sentenceInput, 79, '\n');
cin.ignore(80, '\n');
cout << "\nPlease enter another sentence!\n" << endl;
cin.get(sentence2.sentenceInput, 79, '\n');
cout << "\nThe first sentence reversed: ";
sentence1.reverse_sentence();
cout << endl;
cout << "The second sentence where each word is reversed: ";
sentence2.reverse_words();
cout << endl;
cout << endl;
cout << "The first sentence where each word is reversed: ";
sentence1.reverse_words();
cout << endl;
cout << "The second sentence reversed: ";
sentence2.reverse_sentence();
cout << endl;
return 0;
}
void cStringType::reverse_sentence()
{
char reverse_sentence;
//Reverse entire sentence using loop
for (int i = 0; i < strlen(sentenceInput) / 2; i++)
{
//Reverse the sentence using the length of the
//variable in the class
reverse_sentence = sentenceInput[i];
//First get the user input
//Set your variable equal to the variable in the class
sentenceInput[i] = sentenceInput[strlen(sentenceInput) - i - 1];
//Then reverse the characters and word order
//Starts from the last character in the array
//and goes backwards to 0
sentenceInput[strlen(sentenceInput) - i - 1] = reverse_sentence;
//Set the variable equal to the result
//sentenceInput is now the reverse of the user input in main
}
cout << sentenceInput << endl;
//Output of the new sentence
}
void cStringType::reverse_words()
{
int beginning, end, j = 0;
char reverse_words;
//Reverse each word separately using loop
for (int i = 0; i <= strlen(sentenceInput); i++)
//Get the length of the sentence in the class
{
if (sentenceInput[i] == ' ' || sentenceInput[i] == '\0')
//Check for spaces or null characters
//This allows only the letters of each word to be
//reversed, not the entire sentence
{
for (beginning = j, end = i - 1;
beginning < (i + j) / 2; beginning++, end--)
//j is the beginning of the array; increases
//i is the end of the array; decreases
{
reverse_words = sentenceInput[beginning];
//Set a variable equal to the first
//word in the original user input
sentenceInput[beginning] = sentenceInput[end];
//Set the first letter of a word equal to
//the last letter of a word
sentenceInput[end] = reverse_words;
//Set the result equal to the variable
//sentenceInput is now the user input where each
//word is reversed
}
}
j = i + 1;
}
cout << sentenceInput << endl;
//Output of the new sentence
}
When I try to run the code, the output becomes something like this:
Please enter a sentence!
Hi There
Please enter another sentence!
To Be
The first sentence reversed: erehT iH
The second sentence where each word is reversed: oT eB
The first sentence where each word is reversed: There Hi
The second sentence reversed: Be To
I tried fixing it, but to no avail. The output is never correct.
Is there some way to fix this issue? Or better yet, to simplify the code? I believe the issue is with the code in the function.
The main problem with your code is that it's using the same buffer for both transformations. In other words: you are reversing the words in the same string which you've already reversed entirely. So you need to have another copy of the original string to do these independently.
Regarding simplifying your code you need to define a function that would reverse a string given a pointer and size or begin and end pointers. Then you can use this function on your entire string or on every word you find while searching for a space character:
char *begin = sentenceInput; //points to the beginning of the word
char *end = sentenceInput + strlen(sentenceInput);
for (char *it = begin; it != end; ++it)
if (*it == ' ') {
reverse(begin, it);
begin = it + 1;
}
reverse(begin, end); //reverse the last word
The reverse function can be either std::reverse, which can be used in the above code and on the entire string as follows:
std::reverse(sentenceInput, sentenceInput + strlen(sentenceInput))
or you can create a similar function like this:
void reverse(char *begin, char *end)
{
--end; //point to the last character instead of one-past-last
while (begin < end)
std::swap(*begin++, *end--);
}
I would suggest using stack for it, it is a natural way of looking at it.
so
#include <stack>
and then the function would be like that
void cStringType::reverse_words()
{
int beginning, end, j = 0;
char reverse_words;
stack<char> lastWord;
//Reverse each word separately using loop
for (int i = 0; i <= strlen(sentenceInput); i++)
//Get the length of the sentence in the class
{
if (sentenceInput[i] == ' ' || sentenceInput[i] == '\0')
//Check for spaces or null characters
//This allows only the letters of each word to be
//reversed, not the entire sentence
{
//we want to print the last word that was parsed
while(!lastWord.empty())
{
//we print in the reverse order the word by taking off the stack char by char
cout<< lastWord.top();
lastWord.pop();
}
cout<<" ";
}
//if the letter is not space or end of string then push it on the stack
else
lastWord.push(sentenceInput[i]);
j = i + 1;
}
cout << sentenceInput << endl;
//Output of the new sentence
}

Trouble with dynamic arrays and string occurence (C++)

I am working on a lab for my C++ class. I have a very basic working version of my lab running, however it is not quite how it is supposed to be.
The assignment:
Write a program that reads in a text file one word at a time. Store a word into a dynamically created array when it is first encountered. Create a parallel integer array to hold a count of the number of times that each particular word appears in the text file. If the word appears in the text file multiple times, do not add it into your dynamic array, but make sure to increment the corresponding word frequency counter in the parallel integer array. Remove any trailing punctuation from all words before doing any comparisons.
Create and use the following text file containing a quote from Bill Cosby to test your program.
I don't know the key to success, but the key to failure is trying to please everybody.
At the end of your program, generate a report that prints the contents of your two arrays in a format similar to the following:
Word Frequency Analysis
Word Frequency
I 1
don't 1
know 1
the 2
key 2
...
I can figure out if a word repeats more than once in the array, but I cannot figure out how to not add/remove that repeated word to/from the array. For instance, the word "to" appears three times, but it should only appear in the output one time (meaning it is in one spot in the array).
My code:
using namespace std;
int main()
{
ifstream file;
file.open("Quote.txt");
if (!file)
{
cout << "Error: Failed to open the file.";
}
else
{
string stringContents;
int stringSize = 0;
// find the number of words in the file
while (file >> stringContents)
{
stringSize++;
}
// close and open the file to start from the beginning of the file
file.close();
file.open("Quote.txt");
// create dynamic string arrays to hold the contents of the file
// these will be used to compare with each other the frequency
// of the words in the file
string *mainContents = new string[stringSize];
string *compareContents = new string[stringSize];
// holds the frequency of each word found in the file
int frequency[stringSize];
// initialize frequency array
for (int i = 0; i < stringSize; i++)
{
frequency[i] = 0;
}
stringContents = "";
cout << "Word\t\tFrequency\n";
for (int i = 0; i < stringSize; i++)
{
// if at the beginning of the iteration
// don't check for the reoccurence of the same string in the array
if (i == 0)
{
file >> stringContents;
// convert the current word to a c-string
// so we can remove any trailing punctuation
int wordLength = stringContents.length() + 1;
char *word = new char[wordLength];
strcpy(word, stringContents.c_str());
// set this to no value so that if the word has punctuation
// needed to remove, we can modify this string
stringContents = "";
// remove punctuation except for apostrophes
for (int j = 0; j < wordLength; j++)
{
if (ispunct(word[j]) && word[j] != '\'')
{
word[j] = '\0';
}
stringContents += word[j];
}
mainContents[i] = stringContents;
compareContents[i] = stringContents;
frequency[i] += 1;
}
else
{
file >> stringContents;
int wordLength = stringContents.length() + 1;
char *word = new char[wordLength];
strcpy(word, stringContents.c_str());
// set this to no value so that if the word has punctuation
// needed to remove, we can modify this string
stringContents = "";
for (int j = 0; j < wordLength; j++)
{
if (ispunct(word[j]) && word[j] != '\'')
{
word[j] = '\0';
}
stringContents += word[j];
}
// stringContents = "dont";
//mainContents[i] = stringContents;
compareContents[i] = stringContents;
// search for reoccurence of the word in the array
// if the array already contains the word
// don't add the word to our main array
// this is where I am having difficulty
for (int j = 0; j < stringSize; j++)
{
if (compareContents[i].compare(compareContents[j]) == 0)
{
frequency[i] += 1;
}
else
{
mainContents[i] = stringContents;
}
}
}
cout << mainContents[i] << "\t\t" << frequency[i];
cout << "\n";
}
}
file.close();
return 0;
}
I apologize if the code is difficult to understand/follow through. Any feedback is appreciated :]
If you use stl, the entire problem can be solved easily, with less coding.
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <algorithm>
using namespace std;
int main()
{
ifstream file("Quote.txt");
string aword;
unordered_map<string,int> wordFreq;
if (!file.good()) {
cout << "Error: Failed to open the file.";
return 1;
}
else {
while( file >> aword ) {
aword.erase(remove_if(aword.begin (), aword.end (), ::ispunct), aword.end ()); //Remove Punctuations from string
unordered_map<string,int>::iterator got = wordFreq.find(aword);
if ( got == wordFreq.end() )
wordFreq.insert(std::make_pair<string,int>(aword.c_str(),1)); //insert the unique strings with default freq 1
else
got->second++; //found - increment freq
}
}
file.close();
cout << "\tWord Frequency Analyser\n"<<endl;
cout << " Frequency\t Unique Words"<<endl;
unordered_map<string,int>::iterator it;
for ( it = wordFreq.begin(); it != wordFreq.end(); ++it )
cout << "\t" << it->second << "\t\t" << it->first << endl;
return 0;
}
The algorithm that you use is very complex for such a simple task. Here is what you sahll do:
Ok, first reading pass for determining the maximum size of the
array
Then second reading pass, look directly at what to do: if string is already in the table just increment its frequency, otherwise add it to the table.
Output the table
The else block of your code would then look like:
string stringContents;
int stringSize = 0;
// find the number of words in the file
while (file >> stringContents)
stringSize++;
// close and open the file to start from the beginning of the file
file.close();
file.open("Quote.txt");
string *mainContents = new string[stringSize]; // dynamic array for strings found
int *frequency = new int[stringSize]; // dynamic array for frequency
int uniqueFound = 0; // no unique string found
for (int i = 0; i < stringSize && (file >> stringContents); i++)
{
//remove trailing punctuations
while (stringContents.size() && ispunct(stringContents.back()))
stringContents.pop_back();
// process string found
bool found = false;
for (int j = 0; j < uniqueFound; j++)
if (mainContents[j] == stringContents) { // if string already exist
frequency[j] ++; // increment frequency
found = true;
}
if (!found) { // if string not found, add it !
mainContents[uniqueFound] = stringContents;
frequency[uniqueFound++] = 1; // and increment number of found
}
}
// display results
cout << "Word\t\tFrequency\n";
for (int i=0; i<uniqueFound; i++)
cout << mainContents[i] << "\t\t" << frequency[i] <<endl;
}
Ok, it's an assignment. So you have to use arrays. Later you could sumamrize this code into:
string stringContents;
map<string, int> frequency;
while (file >> stringContents) {
while (stringContents.size() && ispunct(stringContents.back()))
stringContents.pop_back();
frequency[stringContents]++;
}
cout << "Word\t\tFrequency\n";
for (auto w:frequency)
cout << w.first << "\t\t" << w.second << endl;
and even have the words sorted alphabetically.
Depending on whether or not your assignment requires that you use an 'array', per se, you could consider using a std::vector or even a System::Collections::Generic::List for C++/CLI.
Using vectors, your code might look something like this:
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int wordIndex(string); //Protoype a function to check if the vector contains the word
void processWord(string); //Prototype a function to handle each word found
vector<string> wordList; //The dynamic word list
vector<int> wordCount; //The dynamic word count
void main() {
ifstream file("Quote.txt");
if (!file) {
cout << "Error: Failed to read file" << endl;
} else {
//Read each word into the 'word' variable
string word;
while (!file.eof()) {
file >> word;
//Algorithm to remove punctuation here
processWord(word);
}
}
//Write the output to the console
for (int i = 0, j = wordList.size(); i < j; i++) {
cout << wordList[i] << ": " << wordCount[i] << endl;
}
system("pause");
return;
}
void processWord(string word) {
int index = wordIndex(word); //Get the index of the word in the vector - if the word isn't in the vector yet, the function returns -1.
//This serves a double purpose: Check if the word exsists in the vector, and if it does, what it's index is.
if (index > -1) {
wordCount[index]++; //If the word exists, increment it's word count in the parallel vector.
} else {
wordList.push_back(word); //If not, add a new entry
wordCount.push_back(1); //in both vectors.
}
}
int wordIndex(string word) {
//Iterate through the word list vector
for (int i = 0, j = wordList.size(); i < j; i++) {
if (wordList[i] == word) {
return i; //The word has been found. return it's index.
}
}
return -1; //The word is not in the vector. Return -1 to tell the program that the word hasn't been added yet.
}
I've tried to annotate any new code/concepts with comments to make it easy to understand, so hopefully you can find it useful.
As a side note, you may notice that I've moved a lot of the repetative code out of the main function and into other functions. This allows for more efficient and readable coding because you can divide each problem into easily manageable, smaller problems.
Hope this can be of some use.

Anagram generator for C++ (not using STL)

I am trying to create an anagram solver just using a very basic, procedural approach. I am finding out that I probably should have done this using classes, but now it is too late and my assignment is about due. Any suggestions on how to figure this out would be great!
Basically, this is what the algorithm should do:
Get all words in the dictionary; store them in a container
Get a word from the user; quit if appropriate
Get all permutations of the word that the user entered
Strip the word the user entered from the permutations
Strip all words in the permutation collection that aren't also in the dictionary I collected in part 1
Now for the last step, I must make sure that I don't display duplicate anagrams (i.e. anagrams which contain the same letter, such as "loop"). I cannot seem to get this check to work, which is noted below with under the TODO comment block.
Any suggestions would be awesome!!
#include <iostream>
#include <fstream>
#include <string>
//
// Change size below to accomodate more anagrams and dictionary words
//
#define MAX_ANGM_SIZE 4096
#define MAX_WORD_SIZE 1048576
using namespace std;
//
// Determines whether anagram is valid or not; will not display word
// which user entered or words not contained in dictionary
//
bool isValidAnagram(string word, string userWord,
string dictionary[], unsigned int listIdx)
{
for(unsigned int idx = 0; idx < listIdx; ++idx)
{
if(word == userWord)
return false;
else if (word == dictionary[idx])
return true;
}
return false;
}
//
// Determines whether user's word is contained in the dictionary
// or not
//
bool isValidWord(string word, string dictionary[],
unsigned int listIdx)
{
for(unsigned int idx = 0; idx < listIdx; ++idx)
{
if(word == dictionary[idx])
return true;
}
return false;
}
//
// TODO:This function should test for duplicate anagrams and return
// true if duplicates are found.
//
bool isRepeated(string anagrams[], unsigned int anaIdx)
{
for(unsigned int idx = anaIdx; idx != 0; --idx)
{
if(anagrams[idx] == anagrams[anaIdx])
return true;
else
return false;
}
return false;
}
//
// Only display elements in array which aren't blank and don't
// display duplicate anagrams; notify user if no anagrams
// were found.
//
void displayAnagrams(string anagrams[], unsigned int next)
{
int flag = 0;
for (unsigned int idx = 0; idx < next; ++idx)
{
if((anagrams[idx] != "") || (!(isRepeated(anagrams, idx))))
{
if(idx == 1)
cout << " Anagrams: ";
if(idx > 0)
flag = 1;
cout << anagrams[idx] << " ";
}
else
continue;
}
if(flag == 0)
cout << " no anagrams found" << endl;
}
static void swap(char &c1, char &c2)
{
char temp = c1;
c1 = c2;
c2 = temp;
}
//
// Pass in word to be altered, the userWord for comparison, the array to store
// anagrams, the dictionary for comparison, the count for the number of anagrams
// and the count for number of dictionary words
//
static void permute(string word, string userWord, int k, string anagrams[],
string dictionary[], unsigned int &next, unsigned int listIdx)
{
if(k == word.length()-1)
{
if(isValidAnagram(word, userWord, dictionary, listIdx))
anagrams[next] = word;
++next;
}
else
{
for(int idx = k; idx < word.length(); ++idx)
{
swap(word[k], word[idx]);
permute(word, userWord, k+1, anagrams, dictionary, next, listIdx);
}
}
}
//
// Create container to store anagrams, validate user's word in dictionary, get all
// of the anagrams, then display all valid anagrams
//
void getAnagrams(string word, string dictionary[], unsigned int listIdx)
{
string anagrams[MAX_ANGM_SIZE];
unsigned int next = 0;
if(isValidWord(word, dictionary, listIdx))
{
permute(word, word, 0, anagrams, dictionary, next, listIdx);
}
else
{
cerr << " \"" << word << "\"" << " is not a valid word" << endl;
return;
}
displayAnagrams(anagrams, next);
}
//
// Read in dictionary file, store contents of file in a list, prompt
// the user to type in words to generate anagrams
//
int main()
{
string file;
string word;
string quit = "quit";
string dictionary[MAX_WORD_SIZE];
unsigned int idx = 0;
cout << "Enter a dictionary file: ";
cin >> file;
cout << "Reading file \"" << file << "\"" << endl;
cout << endl;
ifstream inFile(file.c_str());
if(!(inFile.is_open()))
{
cerr << "Can't open file \"" << file << "\""
<< endl;
exit(EXIT_FAILURE);
}
while(!inFile.eof())
{
inFile >> dictionary[idx];
++idx;
}
inFile.close();
while(true)
{
cout << "Enter a word: ";
cin >> word;
if(word == quit) break;
getAnagrams(word, dictionary, idx);
cout << endl;
}
return 0;
}
You may want to rethink your step (3). If the user enters a 12-letter word you have 479,001,600 permutations of it which will probably be impractical to assemble all at once (and if that's not, then a 16-letter word will be...).
Instead, try thinking about how you could store the words and look up potential anagrams in a way that doesn't require you to do that.
Edit: I get that ability to solve largeish words may not be your biggest concern at this point, but it might actually make your fourth and fifth steps easier if you do them by assembling the set of valid words rather than starting with all possibilities and removing all the ones that don't match. 'Removing' an item from an array is a bit awkward since you have to shuffle all the following items up to fill in the gap (this is exactly the kind of thing that STL manages for you).
Better algorithm : don't store your word, but store a tupple containing (your word, sorted letters). Moreover, you sort that big storage by the second key (hint, you could use a sqlite database to do the work for you and use an index (can't be unique!))
E.g. to store
"Florent", "Abraham","Zoe"
you would store in memory
("aaabhmr", "abraham"),("eflnort","florent"),("eoz","zoe")
When you got your word from your user, you just use same "sorting letter inside word" algorithm.
Then you look for that pattern in your storage, and you find all anagrams very quickly (log(size of dictionary)) as it's sorted. Of course, original words are the second elements of your tuple.
You can do that using classes, standard structures, a database, up to you to choose the easiest implementation (and the one fitting your requirements)