I am to write a program that will take 2 strings, put them into a function called "build_histogram" and build a int array that keeps count of how many times each letter appears in each string, then compare the arrays and if they are equal, then they are anagrams. The instructions state we are to ignore all symbols(ex. !, whitespaces, _, etc.) and it is not to be case sensitive.
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
void build_histogram(int letters[], string s) {
for(int i = 0; i < s.length(); i++) {
letters[s[i]]++;
}
}
int main()
{
string s1, s2;
int* histogram1[26];
int* histogram2[26];
cout << "Enter two strings." << endl;
getline(cin, s1);
getline(cin, s2);
build_histogram(histogram1[26], s1);
build_histogram(histogram2[26], s2);
if(histogram1 != histogram2) {
cout << "They are not anagrams." << endl;
}
else {
cout << "They are anagrams!" << endl;
}
return 0;
}
This is what I have so far, but no matter what strings I enter, I cannot get the program to print anything besides "Enter two strings."
EDIT
So this is my code now...it counts the number of characters correctly in each string, the only issue now is the "if else" statement at the bottom still doesn't recognize that the arrays are the same, also it's having a hard time when symbols like '!' are in the strings.
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
void build_histogram(int letters[], string s) {
for(int i = 0; i < s.length(); i++) {
char currLetter = s[i];
currLetter = tolower(currLetter);
int index = currLetter - 97;
letters[index]++;
}
}
int main()
{
string s1, s2;
int histogram1[26] = {0};
int histogram2[26] = {0};
cout << "Enter two strings." << endl;
getline(cin, s1);
getline(cin, s2);
build_histogram(histogram1, s1);
build_histogram(histogram2, s2);
if (histogram1 == histogram2) {
cout << "They are not anagrams." << endl;
} else {
cout << "They are anagrams!" << endl;
}
return 0;
}
Two strings are anagrams if they have the same length and one std::is_permutation() of the other.
See http://en.cppreference.com/w/cpp/algorithm/is_permutation
One could write a function to check if two strings are anagrams like this:
bool is_anagram(const std::string& s1, const std::string& s2) {
return s1.size() == s2.size() &&
std::is_permutation(std::begin(s1), std::end(s1),
std::begin(s2), std::end(s2));
}
This isnt your whole solution but it is definitely a start.
Your letters[] array only has indexes 0 through 25, therefore you'd want to convert your letters to correspond with 0-25.
Here is an easy way to do so:
void build_histogram(int* letters[], string s) {
for(int i = 0; i < s.length(); i++) {
char currLetter = s[i];
///force letter to become lowercase
currLetter = tolower(currLetter);
///make letter an ASCII value
int index = currLetter - 97;//subtract 97 to get a range from 0 to 25
letters[index]++;
}
}
This implementation makes all letters into their lowercase forms.
Additionally, the number of instances of 'a' would be stored in index 0, and 'z' in index 25.
Hopefully this helps you go down the right track!
Related
I need to write a function that gets a string and count how many words there are in the string and how many letters. And then calculate the average of it.
A word in a string is a sequence of letters and numbers separated by one or more spaces.
First of all I have to check if the string is correct. The string must contain only lowercase letters, uppercase letters, and numbers only.
i didnt menage to count all sort of words correctly and also my function doesnt count the last letter.
#include <iostream>
using namespace std;
#include <string.h>
#define SIZE 50
float checkString(char string[]) {
float wordCounter = 0;
float letterCounter = 0;
bool isLegit = true;
int i = 0;
while (isLegit) {
if (((string[i] >= 48 && string[i] <= 57) ||
(string[i] >= 65 && string[i] <= 90) ||
(string[i] >= 97 && string[i] <= 122 ))) {
for (int j = 0; j <= strlen(string); j++) {
if ((string[j - 1] != ' ' && string[j] == ' ' &&
string[i + 1] != ' ')
|| j == (strlen(string) - 1)) {
wordCounter++;
}
else if (string[j] != ' ') {
letterCounter++;
cout << string[j];
}
}
cout << " The avareage is : " << (letterCounter /
wordCounter) << endl;
isLegit = false;
}
else {
return -1;
isLegit = false;
}
}
cout << "Number of words " << wordCounter << endl;
cout << "Number of letters " <<letterCounter << endl;
}
int main() {
char string[SIZE];
cout << "please enter a sentence " << endl;
cin.getline(string, SIZE);
checkString(string);
}
Instead of using char[] for strings, I suggest that you use std::string which can grow and shrink dynamically. It's one of the most common types to use in the standard C++ library. You can also make use of stringstreams which lets you put a string inside it and then you can extract the contents of the stringstream using >>, just like when reading from std::cin.
Example with comments in the code:
#include <iostream>
#include <sstream> // std::stringstream
#include <string> // std::string
// use std::string instead of a char[]
float checkString(const std::string& string) {
// put the string in a stringstream to extract word-by-word
std::istringstream is(string);
unsigned words = 0;
unsigned letters = 0;
std::string word;
// extract one word at a time from the stringstream:
while(is >> word) {
// erase invalid characters:
for(auto it = word.begin(); it != word.end();) {
// Don't use magic numbers. Put the character literals in the code so
// everyone can see what you mean
if((*it>='0' && *it<='9')||(*it>='A' && *it<='Z')||(*it>='a' && *it<='z')) {
// it was a valid char
++it;
} else {
// it was an invalid char, erase it
it = word.erase(it);
}
}
// if the word still has some characters in it, make it count:
if(word.size()) {
++words;
letters += word.size();
std::cout << '\'' << word << "'\n"; // for debugging
}
}
std::cout << "Number of words " << words << "\n";
std::cout << "Number of letters " << letters << "\n";
std::cout << "The average number of letters per word is "
<< static_cast<float>(letters) / words << '\n';
return 0.f; // not sure what you are supposed to return, but since the function
// signature says that you should return a float, you must return a float.
}
int main() {
checkString(" Hello !!! World, now let's see if it works. ");
}
I would like to add an additional answer. This answer is based on "more-modern" C++ and the usage of algorithms. You want to solve 3 tasks:
Check, if string is OK and matched to your expectations
Count the number of words in the given string
Count the number of letters
Calculate the ratio of words/letters
For all this you may use existings algorithms from the C++ standard library. In the attached example code, you will see a one-liner for each task.
The statements are somehow very simple, so that I will not explain much more. If there should be a question, I am happy to answer.
Please see here one possible example code:
#include <iostream>
#include <string>
#include <iterator>
#include <regex>
#include <algorithm>
#include <tuple>
#include <cctype>
std::regex re("\\w+");
std::tuple<bool, int, int, double> checkString(const std::string& str) {
// Check if string consists only of allowed values, spaces or alpha numerical
bool stringOK{ std::all_of(str.begin(), str.end(), [](const char c) { return std::isalnum(c) || std::isspace(c); }) };
// Count the number of words
int numberOfWords{ std::distance(std::sregex_token_iterator(str.begin(),str.end(), re, 1), {}) };
// Count the number of letters
int numberOfLetters{ std::count_if(str.begin(), str.end(), isalnum) };
// Return all calculated values
return std::make_tuple(stringOK, numberOfWords, numberOfLetters, static_cast<double>(numberOfWords)/ numberOfLetters);
}
int main() {
// Ask user to input string
std::cout << "Please enter a sentence:\n";
// Get string from user
if (std::string str{}; std::getline(std::cin, str)) {
// Analyze string
auto [stringOk, numberOfWords, numberOfLetters, ratio] = checkString(str);
// SHow result
std::cout << "\nString content check: " << (stringOk ? "OK" : "NOK") << "\nNumber of words: "
<< numberOfWords << "\nNumber of letters: " << numberOfLetters << "\nRatio: " << ratio << "\n";
}
return 0;
}
Of course there are many more other possible solutions. But, because of the simplicity of this solution, I showed this variant.
I'm new to C++ and coding. I tried to make a hangman game as a beginner project. The problem I have is that the game only works when the letters of the word is typed in order. If the word is "flow" for instance, I have to type each letter consecutively (f,l,o,w). Any other variations is not accepted and I don't know why. I need help debugging this issue. I'm not sure if .replace is the method I should be using here. I found this method on the internet and I thought it would do what I needed it to do.
#include <iostream>
#include <string>
#include <time.h>
#include <stdlib.h>
using namespace std;
string getString(char guess) {
string s(1, guess);
return s;
}
int main() {
unsigned int seed;
int randomNumber = 0;
char guess;
string underscore;
seed = time(0);
cout << "Hangman game\n";
srand(seed);
randomNumber = (rand() % 5);
string wordList[5] = {"closet", "flow", "sheep", "see", "chocolate"};
string word = wordList[randomNumber];
int wordLength = word.length();
cout << "The word has " << wordLength << " letters\n";
for (int x = 0; x < wordLength; x++) {
underscore += "_ ";
}
cout << underscore << endl;
string holder = underscore;
for (int j = 0; j < wordLength; j++) {
cout << "\n\nType in a letter: ";
cin >> guess;
if (guess == word[j]) {
size_t found = word.find(guess);
holder.replace(found, 2, getString(guess));
cout << "\n";
word.replace(found, 1, "*");
cout << holder;
}
else {
}
}
return 0;
}
Here are some observations that might help you:
Don’t declare all variables at the top of your function. Declare them as you need them.
Avoid hard-coding (wordList[5]). Add as many as strings as you need in your array. Use the following to find out how many are (see sizeof):
string wordList[] = { "closet", "flow", "sheep", "see", "chocolate" };
size_t wordCount = sizeof wordList / sizeof wordList[0];
You do not need to manually fill the underscore string. Use the following constructor:
string underscore(word.length(), '_');
The user might enter uppercase letters. Convert them to lowercase. Otherwise you will not find them:
guess = tolower(guess);
You do not need fancy functions to find out where the entered character is located. Just use a loop:
//...
bool found = true;
for (int i = 0; i < word.length(); i++)
{
if (word[i] == guess) // is the letter at position i the same as the one entered?
underscore[i] = guess; // replace it
if (underscore[i] == '_')
found = false;
}
if (found)
{
cout << "Good job!" << endl;
return 0;
}
//...
When I print out text2 I see that it is definitely not the reverse of the string I gave it and I'm not sure why that is. When I put in "test" I get stuff like "ȍ\2200+". Can I use strncpy on char arrays? Maybe it needs to be done with a loop - not sure. Any help would be appreciated. :)
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
char text[79], text2[79];
bool input = true;
while (input) {
cout << "Please give me a line of text to examine: ";
cin.getline(text, 79);
for(int i = 0; i < strlen(text); i++ )
cout << text[i];
// test to see if it is a palindrome
strncpy(text, text2, 80);
reverse(text2, text2 + strlen(text2));
printf("%s", text2); `// when I print this out I get something odd`
if (strcmp(text, text2) == 0)
cout << " is a palindrome!" << endl;
else
cout << " is not a palindrome." << endl;
if (strcmp(text, "END") == 0)
input = false;
else
cout << "\ntype END to exit the program" << endl;
} // end while loop
} // end main
It seems you're using strncpy in a wrong way: you probably want to copy text into text2, not the other way around.
There's a much simpler way to test whether a string is a palindrome, namely:
bool is_palindrome(const char* s, size_t n) {
size_t i, j;
i = 0, j = n-1;
while (i < j && s[i++] == s[j--])
;
return i >= j;
}
Why not use std::vector<char> and std::reverse from <algorithm> to handle your problem?
I would do something like below: (note that I'm using C++11 range-based for loop and auto which you can change to a regular for loop and use std::string line if you don't have a compiler supporting this).
int main()
{
cout << "Please give me a line of text to examine: ";
auto line = ""s;
getline(cin, line);
// Push back every character to the vector
vector<char> vtext;
for (const auto &elem : line)
vtext.push_back(elem);
// Create a copy of the vector<char> and reverse the copy
vector<char> vtext_reversed{vtext};
reverse(begin(vtext_reversed), end(vtext_reversed));
// Print the line reversed
cout << "\nThis is the line reversed: ";
for (const auto &elem : vtext_reversed)
cout << elem;
}
Typically you'll see this reversal technique for char*:
void reverse(char* s) {
if(!s) return;
size_t n = strlen(s);
for(size_t i = 0; i < n/2; ++i) {
char tmp = s[i];
s[i] = s[n - i - 1];
s[n - i - 1] = tmp;
}
}
This will not work, however, with non-ASCII characters. The reason is that non-ASCII characters require multiple bytes to represent.
You will need to use wide characters to handle multi-byte codepoints, but the logic should follow above.
This program is supposed to compare the list of consonants to a user input list of letters and print out the number of consonants in the user's input. However, it just prints 0. I'm very new to C++ and am not experienced in finding logic errors.
#include <iostream>
#include <cctype>
#include <string>
using namespace std;
int counter(char *, char);
int main()
{
const int size = 51;
char input[size];
const char consonants[22] = "bcdfghjklmnpqrstvwxyz";
cout << "Enter your letters." << endl;
cin >> input;
cout << consonants << "appears";
cout << counter(input, consonants[22]) << "times" << endl;
}
int counter(char *strPtr, char ch)
{
int times = 0;
while (*strPtr != '\0')
{
if (*strPtr == ch)
times++;
strPtr++;
}
return times;
}
I'm aware you're new to C++, and this looks like some kind of exercise you are doing in order to learn, but I will post this answer so you can see how get this done using some of the C++ standar functions.
Using find function from algorithm
string test = "Hello world";
string vowels("aeiuo"); // Its much easier to define vowels than consonants.
int consonants_count = test.length(); // Assume all letters are consonants.
for (auto &c : test) // for each character in test
{
if (find(vowels.begin(), vowels.end(), c) != vowels.end()) // If c is founded inside vowels ...
{
consonants_count--; // Decrement the number of consonants.
}
}
Using regular expressions
#include <regex>
string test = "Hello world"; // A test string.
regex re("a|e|i|o|u"); // Regular expression that match any vowel.
string result = regex_replace(test, re, ""); // "Delete" vowels.
cout << result.length() << endl; // Count remaining letters.
Three problems:
You are not passing an array of consonants, you are passing a single character
You are passing an invalid character (one past the end of the consonant array)
You are counting how many times that invalid character is present.
To fix this problem, make sure that you pass an array as the second parameter, and add a nested loop to iterate that array.
your function counter if checking the input char by char and compare it to a single char(ch).
you need to run your counter function on all the chars in consonants array, or change the counter function:
int count = 0
for(int i = 0; i < 22 ; i ++)
{
count += counter(input, consonants[i])
}
now an even better way will be to count the non consonants characters and then do length-count
#include <iostream>
#include <cctype>
#include <string>
using namespace std;
int counter(char *, char);
int main()
{
const int size = 51;
char input[size];
cout << "Enter your letters." << endl;
cin >> input;
cout << consonants << "appears";
cout << counter(input) << "times" << endl;
}
int counter(char *strPtr)
{
int times = 0;
int length = 0;
const char consonants[5] = "aeoui";
while (*strPtr != '\0')
{
for(int i = 0; i < 5 ; i ++)
{
if (*strPtr == consonants[i])
times++;
strPtr++;
length++;
}
}
return length-times;
}
I am trying to get the following code to work:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <cmath>
using namespace std;
bool prime_test(int num);
void stringRotation(string& str);
int main()
{
vector<string> primes;
ifstream infile("PRIMES1T.txt");
// checks to see if there was any problems opening the .txt
if (infile.is_open()) {
string line = "";
while(getline(infile,line)) {
primes.push_back(line);
}
// rotates our string and tests if the number is still prime
vector<string> primes2;
for (int i = 0; i < primes.size(); i++) {
string str = primes[i];
for (int j = 0; j < str.length(); j++) {
stringRotation(str);
int value = atoi(str.c_str());
if (prime_test(value) == false) {
break;
}
if (j == str.length()-1) {
if (prime_test(value) == true) {
primes2.push_back(primes[i]);
}
}
}
}
cout << "There are " << primes2.size() << " primes that work.";
cout << endl;
}
else {
cout << "File failed to open." << endl;
}
return 0;
}
// tests to see if num is a prime number
bool prime_test(int num) {
if (num == 1) {
return false;
}
// Finds first integer value larger than the sqrt of num
// since that is all we really need.
double dnum = num;
double sqrt_dnum = sqrt(dnum);
int counter = ceil(sqrt_dnum);
for (int i = 2; i < counter; i++) {
if (num == 2) {
break;
}
if (num%i == 0) {
return false;
}
}
return true;
}
// rotates a string
void stringRotation(string& str) {
int len = str.length();
// converts a char variable into a string variable
stringstream ss;
string ch;
char c = str.at(0);
ss << c;
ss >> ch;
str = str.substr(1,str.length());
str = str.append(ch);
cout << str << endl;
}
What it does is it takes a prime number say 999983, cuts off the first digit 9, and then adds it to the end of the rest of the number so that it spits out the new number 999839. It then tests whether or not this new number is prime or not and repeats the process until the original number is returned. If the number is prime every time we do this process, then we add that number to the vector primes2.
The problem I have is that the stringRotation function does not work properly for some reason. I have tested it by trying to outputting the string before adding the digit that was removed and outputting the string after adding the digit. It does not concatenate properly. It will cut off the first digit in 999983 so that we have str = '99983' and ch = '9' but then when I do str.append(ch), it still gives me 99983. I have also tried variations like str = str.append(ch) and str = str + ch.
I have tried copying just the function over to a different .cpp file to compile only adding a declaration for str by setting str to "999983" and it works fine.
EDIT
I changed stringRotation to:
void stringRotation(string& str) {
int len = str.length();
char ch = str.at(0);
cout << ch << endl;
str = str.substr(1,str.length());
str.append(1,ch);
cout << str << endl;
}
but the problem still persists. I have also tried string.push_back(ch) with no luck.
In your programmer career, you will need to always make sure that your input is handled well. If you are loading data from a file which is not guaranteed to have a specific content scheme, you will always need to make sure that you prepare your data before parsing. In this particular case you need to make sure that your "numbers" are indeed numbers and execute your stringRotation on values which are guaranteed to be numbers.