Finding and printing common letters in different strings - c++

I need to find and print common characters in different strings. My code does not work as it should, it checks for same letters at the same index but thats not what I want. I couldn't find better solution for now. Thank you for help :)
#include <iostream>
#include <string>
using namespace std;
int main() {
string niz1, niz2, niz3 = "";
cout << "string 1: ";
getline(cin, niz1);
cout << "string 2: ";
getline(cin, niz2);
for (int i = 0; i < niz1.length() - 1; i++) {
for (int j = 0; j < niz2.length() - 1; j++) {
if (niz1[i] == niz2[j])
niz3 += niz1[i];
}
}
cout << "Same letters are: " << niz3 << endl;
return 0;
}

Below is corrected working code. Basically the only correction that was needed to do is to make both loops have upper bound of niz.length() instead of niz.length() - 1.
Variant 1:
Try it online!
#include <string>
#include <iostream>
using namespace std;
int main() {
string niz1, niz2, niz3 = "";
cout << "string 1: ";
getline(cin, niz1);
cout << "string 2: ";
getline(cin, niz2);
for (int i = 0; i < niz1.length(); i++) {
for (int j = 0; j < niz2.length(); j++) {
if (niz1[i] == niz2[j])
niz3 += niz1[i];
}
}
cout << "Same letters are: " << niz3 << endl;
return 0;
}
Input:
string 1: adbc
string 2: cde
Output:
Same letters are: dc
Also you may want to sort letters and make them unique, then you need to use std::set too like in code below:
Variant 2:
Try it online!
#include <string>
#include <iostream>
#include <set>
using namespace std;
int main() {
string niz1, niz2, niz3 = "";
cout << "string 1: ";
getline(cin, niz1);
cout << "string 2: ";
getline(cin, niz2);
for (int i = 0; i < niz1.length(); i++) {
for (int j = 0; j < niz2.length(); j++) {
if (niz1[i] == niz2[j])
niz3 += niz1[i];
}
}
set<char> unique(niz3.begin(), niz3.end());
niz3.assign(unique.begin(), unique.end());
cout << "Same letters are: " << niz3 << endl;
return 0;
}
Input:
string 1: adbcda
string 2: cdecd
Output:
Same letters are: cd
Also you may use just sets plus set_intersection standard function. This will solve your task in less time, in O(N*log(N)) time instead of your O(N^2) time.
Variant 3:
Try it online!
#include <string>
#include <iostream>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
int main() {
string niz1, niz2, niz3 = "";
cout << "string 1: ";
getline(cin, niz1);
cout << "string 2: ";
getline(cin, niz2);
set<char> s1(niz1.begin(), niz1.end()), s2(niz2.begin(), niz2.end());
set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), back_inserter(niz3));
cout << "Same letters are: " << niz3 << endl;
return 0;
}
Input:
string 1: adbcda
string 2: cdecd
Output:
Same letters are: cd
Instead of set it is also possible to use unordered_set, it will give even more faster algorithm especially for long strings, algorithm will have running time O(N) compared to O(N * log(N)) for set solution. The only drawback is that unlike for set solution output of unordered_set solution is unsorted (but unique) (unordered sets don't sort their data).
Variant 4:
Try it online!
#include <string>
#include <iostream>
#include <unordered_set>
#include <algorithm>
#include <iterator>
using namespace std;
int main() {
string niz1, niz2, niz3;
cout << "string 1: ";
getline(cin, niz1);
cout << "string 2: ";
getline(cin, niz2);
unordered_set<char> s1(niz1.begin(), niz1.end()), s2;
for (size_t i = 0; i < niz2.length(); ++i)
if (s1.count(niz2[i]))
s2.insert(niz2[i]);
niz3.assign(s2.begin(), s2.end());
cout << "Same letters are: " << niz3 << endl;
return 0;
}
Input:
string 1: adbcda
string 2: cdecd
Output:
Same letters are: dc
Also one more way is to use just plain for loops like you did, without sets, but do extra block of loops in order to remove non-unique letters, like in code below. The only drawbacks of this loops method compared to sets method is that loops method runs slower and produces non-sorted output string.
Variant 5:
Try it online!
#include <string>
#include <iostream>
using namespace std;
int main() {
string niz1, niz2, niz3, niz4;
cout << "string 1: ";
getline(cin, niz1);
cout << "string 2: ";
getline(cin, niz2);
for (int i = 0; i < niz1.length(); ++i)
for (int j = 0; j < niz2.length(); ++j)
if (niz1[i] == niz2[j])
niz3 += niz1[i];
for (int i = 0; i < niz3.length(); ++i) {
bool exists = false;
for (int j = 0; j < niz4.length(); ++j)
if (niz4[j] == niz3[i]) {
exists = true;
break;
}
if (!exists)
niz4 += niz3[i];
}
cout << "Same letters are: " << niz4 << endl;
return 0;
}
Input:
string 1: adbcda
string 2: cdecd
Output:
Same letters are: dc

This is sort of an answer in itself, and sort of an extended comment on #Arty's answer.
Hash tables (which are what underlies an unordered_map) are really useful under many circumstances. But in this case, they're kind of overkill. In particular, a hash table is basically a way of creating a sparse array for cases where it's unrealistic or unreasonable to use the underlying "key" type directly as an index into an array.
In this case, however, what we're using as the key in the hash table is a character--a single byte. This is small enough, it's utterly trivial to just use an array, and use the byte directly as an index into the array.
So, with arrays instead of hash tables, we get code something on this order:
#include <array>
#include <string>
#include <iostream>
#include <chrono>
std::string getstring(std::string const &s) {
std::cout << s << ": ";
std::string input;
std::getline(std::cin, input);
return input;
}
using namespace std::chrono;
int main() {
std::array<char, 256> a = {0};
std::array<char, 256> b = {0};
std::array<char, 256> result = { 0 };
std::size_t pos=0;
std::string s1 = getstring("s1");
std::string s2 = getstring("s2");
std::cout << "s1: " << s1 << "\n";
std::cout << "s2: " << s2 << "\n";
auto start = high_resolution_clock::now();
for (auto c : s1)
a[c] = 1;
for (auto c : s2)
b[c] = 1;
for (int i = 'a'; i < 'z'; i++)
if (a[i] != 0 && b[i] != 0)
result[pos++] = i;
for (int i = 'A'; i < 'Z'; i++)
if (a[i] != 0 && b[i] != 0)
result[pos++] = i;
auto stop = high_resolution_clock::now();
std::cout << "Common characters: " << std::string(result.data(), pos) <<"\n";
std::cout << "Time: " << duration_cast<nanoseconds>(stop - start).count() << " nS\n ";
}
To get some repeatable test conditions, I built an input file with a couple of long fairly strings:
asdffghjllkjpoiuwqertqwerxvzxvcn
qqweroiglkgfpoilkagfskeqwriougfkljzxbvckxzv
After adding instrumentation (timing code) to his Variant 4 code, I found that his code ran in about 12,000 to 12,500 nanoseconds.
The code above, on the other hand, runs (on the same hardware) in about 850 nanoseconds, or around 15 times as fast.
I'm going to go on record as saying the opposite: although this is clearly faster, I'm pretty sure it's not the fastest possible. I can see at least one fairly obvious improvement (store only one bit per character instead of one byte) that would probably improve speed by at least 2x, and could theoretically yield an improvement around 8x or so. Unfortunately, we've already sped other things up enough that I doubt we'd see 8x--we'd probably see a bottleneck on reading in the data from memory first (but it's hard to be sure, and likely to vary between processors). So, I'm going to leave that alone, at least for now. For now, I'll settle for only about fifteen times faster than "the fastest possible solution"... :-)
I suppose, in fairness, he probably really meant asymptotically the fastest. His has (expected, but not guaranteed) O(N) complexity, and mine also has O(N) complexity (but in this case, basically guaranteed, not not just expected. In other words, the 15x is roughly a constant factor, not one we expect to change significantly with the size of the input string. Nonetheless, even if it's "only" a constant factor, 15x is still a pretty noticeable difference in speed.

Related

Non-repeating random numbers

I'm working on a synonym puzzle, it gives you a word and wants you to find the synonym of it given the lenght of the word. Everything works fine, but it happens on an ordered sequence; words do not appear randomly. I need knumber of non-repeating random numbers for that. Here's my code:
#include<iostream>
#include<string>
#include<cstdlib>
#include<ctime>
using namespace std;
int main()
{
const int k=4;
string word[k]={"furious","tiny","untrue", "humorous", "harm"};
string nword[k]={"angry","small","false", "funny", "damage"};
string j;
int point, p = 0;
int ctr=0;
srand(time(NULL));
int randNum = (rand() % k) + 0;
for(int i=0; i<k; i++)
{
cout << nword[i] << "\n";
cout << "The length of the word: " << word[i].length() << "\n";
cin>>j;
ctr++;
if(j==word[i])
{
cout<<"Correct! Score: " << i+1 << " point." << "\n\n";
}
else
{
cout<<"Wrong"<<endl;
}
}
return 0;
}
As you can see, the variable randNum holds the value of the random number from 0 to k, (k is 4, combined with 0, I got 5 words). In for loop, when I set the nword and word like nword[randNum], and word[randNum], the result leaves a lot to be desired. First, I think there's no sync for the two (nword and word). It will apply different random numbers for the two (I might be wrong) and the second, it will be repetitive. As seen, the execution is score-based and completable, so I need non repeating questions until it reaches to k.
You could shuffle your word arrays by using the Durstenfeld shuffle:
for(int i=k-1; i>0; i--)
{
int j = rand() % (i+1)
string temp = word[i];
word[i] = word[j];
word[j] = temp;
temp = nword[i];
nword[i] = nword[j];
nword[j] = temp;
}
As pointed out by WhozCraig, an alternative option (arguably better, as it doesn't require permuting multiple arrays), is to create an array with indices 0..(k-1) and shuffle this array instead. This array would then contain a set of randomised indices which could be used to iterate over your word arrays.
#include <iostream>
#include <algorithm>
#include <string>
#include <random>
#include <numeric>
using namespace std;
int main()
{
static const size_t k=5;
string word[k]={"furious","tiny","untrue", "humorous", "harm"};
string nword[k]={"angry","small","false", "funny", "damage"};
int ctr=0;
// build prng
std::random_device rd;
std::mt19937 rng(rd());
// build index sequence, then shuffle
size_t idx[k];
std::iota(std::begin(idx), std::end(idx), 0);
std::shuffle(std::begin(idx), std::end(idx), rng);
for(auto i : idx)
{
std::string s;
cout << nword[i] << "\n";
cout << "The length of the word: " << word[i].length() << "\n";
if (!(cin>>s))
break;
if(s==word[i])
{
cout<<"Correct! ";
++ctr;
}
else
{
cout<<"Wrong. ";
}
std::cout << "Score: " << ctr << " point(s).\n\n";
}
return 0;
}

Count the appearance of each alphabet in C++

I am trying to write a code in c++ that take string input from user and arrange the string in alphabetical order. Now I want to extend this code to give me output like how many times 'a' appears and so on, but I could not extend it. There may be many ways to deal with this problem, but please if anyone can guide me how to deal with this problem using arrays.
#include <iostream>
#include <sstream>
#include <string>
#include <map>
using namespace std;
int main()
{
cout << " please enter your charactor " << endl;
string ch;
getline(cin, ch);
int i, step, temp;
for (step = 0; step<ch.size() - 1; ++step)
for (i = 0; i<ch.size()- step - 1; ++i)
{
tolower(ch[1]);
if (tolower(ch[i])>tolower(ch[i + 1]))
{
temp = ch[i];
ch[i] = ch[i + 1];
ch[i + 1] = temp;
}
}
// count the appearance of each letter using array
cout << " total lenght of your string's charactor is " << ch.length() << endl;
system("pause");
}
This is all you need
#include <iostream>
#include <string>
using namespace std;
int main()
{
// you could easily use a vector instead of an array here if you want.
int counter[26]={0};
string my_string = "some letters";
for(char c : my_string) {
if (isalpha(c)) {
counter[tolower(c)-'a']++;
}
}
// thanks to #James
for (int i = 0; i < 26; i++)
{
cout << char('a' + i) << ": " << counter[i] << endl;
}
}
subtracting 'a' from a character baselines the letter 'a' to position 0 in the array. You can add the letter 'a' back to the position when printing it back out.
Using the range-based for loop requires c++11, but you can use a traditional for loop instead in the same way.
Technically this only works for languages with 26 or fewer letters in their alphabet...

Permutation Issue

So I have a program here that is supposed to print to the screen permutations of a user input word that can be 4 to 10 characters long and there are supposed to be as many permutations as there are letters in the word. I almost have complete success, but there is one issue. When it prints the permutations, after the first about 2 permutations, it starts to not use all the letters and/or the same letter twice.
For example, if the user input word is "bill", the output is as follows:
llib illb ibll lbii
The fourth is is obviously not correct. The problem is more apparent with words that have more letters. I need it to only use the letters it has once. How do I go about fixing this in my code? Here is my code.
int main(void)
{
string word;
string randword;
string reverse;
int length;
int i = 0;
int j = 0;
string randCH;
cout << "Enter any word 4 to 10 letters in length: ";
cin >> word;
//Checks if word is less than 4 or greater than 10
while (1)
{
/*The code here is in the final program and I know it works. The problem is not here*/
}
length = word.length();
//Uses reverse function
reverse = reverseit(word);
/*reverseit is a function outside of main that makes the word backwards*/
//Prints all permutations
cout << endl << reverse << " ";
for (i = 0; i < word.length() - 1; i++)
{
for (j = 0; j < word.length(); j++)
{
randCH = word.substr(rand() % length, 1);
cout << randCH;
}
cout << " ";
cout << endl;
you can use std::next_permutation which is already built to achieve this:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string word;
cin >> word;
sort(word.begin(),word.end());
do {
cout << word <<endl;
} while(next_permutation(word.begin(),word.end()));
}

std::cout not working inside a for-loop

I'm new to C++, and right now I'm learning from the book called Accelerated C++. I finished the third chapter (vectors), and I came to this exercise:
"Write a program to count how many times each distinct word appears in its input."
After some thinking, I started working on it. I wanted to test the program, but std::cout wasn't working. I put cout << "test"; on a few places in my code to see where's the problem, and the conclusion is that it doesn't work inside the first for-loop. Don't recommend me to use maps to solve the problem, because I'm working on vectors. The variables aren't in English, so I'll translate some for you to know what's going on:
recenica - the sentence; rijec - a word; vel_vektora - size of the vector; duz_recenice - length of the sentence; br_ponavljanja - number of times a word appears in the sentence;
#include <vector>
#include <iostream>
#include <string>
using std::string; using std::vector;
using std::cin; using std::cout;
using std::endl;
int main()
{
string rijec;
vector<string> recenica;
while (cin >> rijec) recenica.push_back(rijec);
cout << endl;
typedef vector<string>::size_type vel_vektora;
vel_vektora duz_recenice = recenica.size();
cout << "test0, ";
for (int i = 0; i < duz_recenice - 1; ++i)
{
cout << "test, !";
int br_ponavljanja = 1;
for (int j = i + 1; j < duz_recenice; ++j)
{
cout << "test2, ";
if (recenica[i] == recenica[j])
{
cout << "test3, ";
++br_ponavljanja;
recenica.erase(recenica.begin() + j);
}
cout << "test4, ";
}
cout << recenica[i] << ": " << br_ponavljanja << endl;
}
cout << "test5, ";
getchar();
return 0;
}
What's the problem with the std::cout?
Add << flush to flush your output buffer (each place).
Or use << endl, which both adds newline and flushes.
There are problems with the code, especially for empty input, but that's what you're out to learn about, so I'll leave you to it! :-)
Cheers & hth.,
I'm afraid the language eludes me in terms of variable names, but this "Works for Me™".
Here is my output (First 3 lines input:)
ytreyert
tyryteter
gdhdfgdf
^Z
test0, test, !test2, test4, test2, test4, ytreyert: 1
test, !test2, test4, tyryteter: 1
test5,
You should definitely try flushing the cout buffers after printing (as per Alf's answer).
I notice that gdhdfgdf is not counted, this is because of this line:
for (int i = 0; i < duz_recenice - 1; ++i)
If you only give 1 input word, this loop will not run, as you do duz_recenice = recenica.size(); before looping.
Changing this line to
for (int i = 0; i < duz_recenice; ++i)
solves this problem.

Palindrome program isn't comparing properly

I'm currently learning about vectors and trying to make a palindrome program using them. This is a simple program and so far, I'm trying to make it identify "I am what am I." as a palindrome properly. This is my program so far:
#include <vector>
#include <string>
#include <iostream>
using namespace std;
vector <string> sentVec;
void getSent(string sent);
void readBackwards(string sent);
int main()
{
string sent;
getSent(sent);
readBackwards(sent);
return 0;
}
void getSent(string sent)
{
cout << "Enter your sentence:" << endl;
getline (cin,sent);
string currentWord, currentLetter;
for (int i = 0; i < sent.length(); i++)
{
currentLetter = sent[i];
if (currentLetter == " ") // inserts word
{
currentWord += sent[i];
sentVec.push_back(currentWord);
currentWord = "";
}
else if (currentLetter == ".") // inserts period
{
sentVec.push_back(currentWord);
currentWord = sent[i];
sentVec.push_back(currentWord);
}
else
{
currentWord += sent[i];
}
}
}
void readBackwards(string sent)
{
string sentForwards, sentBackwards;
// create sentence forwards and backwards without the period.
for (int i = 0; i < sentVec.size() - 1; i++)
{
sentForwards += sentVec[i];
}
for (int j = sentVec.size() - 2; j >= 0; j--)
{
sentBackwards += sentVec[j];
if (j == sentVec.size() - 2)
{
sentBackwards += " ";
}
}
cout << "Sentence forwards is: " << sentForwards << endl;
cout << "Sentence backwards is: " << sentBackwards << endl;
if (sentForwards == sentBackwards)
{
cout << "This sentence reads the same backwards as forwards." << endl;
}
else
{
cout << "This sentence does not read the same backwards as forwards." << endl;
}
}
When I run this program, it prints:
Enter your sentence:
I am what am I.
Sentence forwards is: I am what am I
Sentence backwards is: I am what am I
This sentence does not read the same backwards as forwards.
Why does this not trigger the if loop when comparing the two sentences?
Because sentBackwards isn't the same as sentForwards, because sentBackwards has a trailing whitespace at the end, and thus they aren't the same.
I am unsure how your program detects palindromes, but here is a simple iterative method:
#include <string>
bool isPalindrome(std::string in) {
for (int i = 0; i < in.size() / 2; i++) {
if (in[i] != in[in.size() - 1 - i]) {
return false;
}
}
return true;
}
It returns true if the string passed as an argument is a palindrome
You should not only learn about vector, but also the STL algorithm functions such as std::reverse.
As the other answer given pointed out, one vector has a trailing whitespace. You could have avoided all of that by simply taking the original vector, copying it to another vector, and calling std::reverse. There is no need to write a loop:
void readBackwards()
{
// copy the vector
std::vector<std::string> sentBackwards = sentVec;
// reverse it
std::reverse(sentBackwards.begin(), sentBackwards.end());
// see if they're equal
if (sentVec == sentBackwards)
cout << "This sentence reads the same backwards as forwards." << endl;
else
cout << "This sentence does not read the same backwards as forwards." << endl;
}
This works, since std::vector has an overloaded operator == that compares the items in each of the two vectors and returns true if all items are the same.
In addition to this, reading into a vector can be accomplished much more easily than what you attempted.
#include <sstream>
#include <algorithm>
//...
void getSent(string sent)
{
// remove the periods(s)
auto iter = std::remove_if(sent.begin(), sent.end(), [] (char ch) { return ch == '.';});
sent.erase(iter, sent.end());
// copy the data to a vector
std::istringstream iss(sent);
string currentword;
while ( iss >> currentword)
sentVec.push_back(currentword);
}
Note that we use the std::istringstream to serve as the space delimited parser, alleviating the need to write a loop looking for the space. Also, the std::remove_if algorithm is used to remove any period characters from the string before we start to store the individual strings into a vector.
So basically, the only loop in this whole setup is the while to read from the stream into the vector. Everything else is accomplished by using the algorithm functions, and taking advantage of the various member functions of std::vector (like the overloaded ==)