Comparing C++ input with array values - c++

Over the last couple months I've still been slowly but surely trudging through C++, and I've run into a snag that I've been meaning to figure out. I've tried asking/reading/searching, but I could never find an appropriate answer. Maybe it is simply because the question is sort of difficult to ask.
What I'm trying to do is at the end of my program, have the end sequence compare the input value with values within an Array. Do I have to loop a comparison sequence? Is there an easier way around this?
#include <iostream>
#include <string>
using namespace std;
int main () {
string YesAnswers[5] = {"Y", "YES", "yes" "y"};
string Name;
string YN;
do {
cout << "Enter your name: ";
getline(cin, Name);
cout << "Your name is "<< Name;
cout <<"\nIs this correct? Y\N: ";
cin >> YN;
} while(YN == YesAnswers);
system("Pause");
return 0;
}

You can use std::find() from <algorithm>:
while (std::find(YesAnswers, YesAnswers + 4, YN) != YesAnswers + 4);
Or, if you were to make YesAnswers a vector or some other container instead of an array:
std::vector<std::string> YesAnswers;
while (std::find(YesAnswers.begin(), YesAnswers.end(), YN) != YesAnswers.end());
std::find() looks for an element in a range; if it finds the element, it returns an iterator (or a pointer, in the case of an array) to the found element; if it doesn't find the element, it returns an iterator (or a pointer) to the end of the range.
Note that, as with most (all?) of the standard library algorithms, the range is closed at the beginning but open at the end. That is, the "end" iterator/pointer should point at the element one-past-the-end.

You could use a std::set. Sets are ordered and so are quicker to find things in than a std::vector for large numbers of elements however in your case you only have 4 so it probably makes little or no difference. Since sets are designed for looking things up quickly they actually have their own find member so you don't have to use std::find. So define YesAnswers as
set< string > YesAnswers;
YesAnswers.insert( "Y" );
YesAnswers.insert( "YES" );
//... and so on
and then check YN with
YesAnswers.find( YN ) != YesAnswers.end()
I still think #James' answer is better for your needs though as you can keep YesAnswers as an array with its one line initialisation as opposed to the multi-line insertions for a set.

In general case, if You want to find if some value is a member of some collection, You have to loop or use some algorithm or collection member functions as other answers suggest.
In Your case there are only four possibilities, so theoretically You could simply check all of them explicitly in a condition using logical OR (operator ||).
The best solution for Your particular problem in my opinion, is to take an input form the user, convert it to upper-case (check for example here how) and compare it with just two strings "Y" or "YES".

A few things: You can declare variables of the same type on the same line, for example:
string Name, YN;
You can actually declare a boolean, initialized to True for your loop and change it to False given a set of conditions. ** (C++ treats any non-0 value as True and 0 as False, so you can actually use an Integer as a boolean value if you want to.)
Bool IsYourName = True;
do
{
...
IsYourName = False;
for (int i = 0; i < YesAnswers.size(); i++)
if (YN == YesAnswers[i])
IsYourName = True;
}
while (IsYourName);
That's another way to think about approaching the loop

Related

why doesn't user defined function sort the elements of same length in the order given?

My task is to sort the words of a string in the increasing order of their length and for words of same length, I have to keep them in the order given.
for ex: "to be or not to be" will become "to be or to be not".
i am first making a vector 'v' of all the words in the string and then trying to sort the vector using user defined function in sort() function of C++.
Here is my code:
#include <bits/stdc++.h>
using namespace std;
static bool comparelength(string first,string second){//function to compare length
return first.size()<second.size();
}
int main() {
string text="Jlhvvd wfwnphmxoa qcuucx qsvqskq cqwfypww dyphntfz hkbwx xmwohi qvzegb ubogo sbdfmnyeim tuqppyipb llwzeug hrsaebveez aszqnvruhr xqpqd ipwbapd mlghuuwvec xpefyglstj dkvhhgecd kry";
vector<string> v;
string cur="";
text+=" ";
for(int i=0;i<text.size();i++){
if(text[i]==32){//if space is encountered then the word is inserted in the vector
v.push_back(cur);
cur="";
}
else{
cur+=text[i];//if not space then text[i] is added to the current word
}
}
sort(v.begin(),v.end(),comparelength);//sort the vector
for(int i=0;i<v.size();i++)
cout<<v[i]<<" ";
Now it gives this output:
"Kry xqpqd ubogo hkbwx qvzegb jlhvvd xmwohi qcuucx qsvqskq llwzeug ipwbapd dyphntfz cqwfypww tuqppyipb dkvhhgecd sbdfmnyeim xpefyglstj mlghuuwvec aszqnvruhr hrsaebveez wfwnphmxoa"
But the correct output should be:
"Kry hkbwx ubogo xqpqd jlhvvd qcuucx xmwohi qvzegb qsvqskq llwzeug ipwbapd cqwfypww dyphntfz tuqppyipb dkvhhgecd wfwnphmxoa sbdfmnyeim hrsaebveez aszqnvruhr mlghuuwvec xpefyglstj"
see the position 1,2 and 3(using 0 indexing).
it should give: hkbwx ubogo xqpqd.
but it gives: xqpqd ubogo hkbwx.
which makes me think that it is not sorting the words of same length in the order given. You can find many other positions where this happens(for ex: 4,5,6 and 7).
But for the string "leetcode plus try suck geaser is cool best"
it gives the correct output which is: "is try plus suck cool best geaser
leetcode"
Can anyone make it clear why is it not working for the former string but working for the latter.
I've tried doing
static bool comparelength(string first,string second){
if(first.size()==second.size())
return true;
if(first.size()<second.size())
return true;
else
return false;
}
But this throws runtime error.
sorry for making the question messy but i really want to understand this.
std::sort is not stable, ie order of elements that are equivalent is not necessarily preserved. If you get a stable sorting from std::sort then this is just by chance. Stable sorting is more expensive (O(N·log(N)^2) vs O(N·log(N))), hence you have to explicitly ask for it. It can be done with std::stable_sort.
You could use std::sort with a custom comparator if you would populate a container of std::pair<std::string,size_t> where second is the index in the original container. However, I suppose using std::stable_sort is simpler.

Splitting up a string from end to start into groups of two in C++

I was curious about the way I could make a program, that takes a string, then detects the end of it, and then starts splitting it up "from end toward the start", into the groups of two?
For instance, the user enters mskkllkkk and the output has to be m sk kl lk kk.
I tried to search the net for the tools I needed, and got familiar with iterators, and tried to use them for this purpose. I did something like this:
#include "iostream"
#include "string"
#include "conio.h"
int main() {
int k=0,i=-1;
std::string str1;
std::string::iterator PlaceCounter;
std::cin >> str1;
PlaceCounter = str1.end();
for (PlaceCounter; PlaceCounter != str1.begin(); --PlaceCounter)
{
++k;
if (k % 2 == 0 && k-1 != 0) {
++i;
str1.insert(str1.end()-k-i,' ');
}
}
std::cout << str1;
_getch();
return 0;
}
At first, it seemed to be working just fine when I entered a couple of arbitrary cases(Such thing can exactly be used in calculators to make the numbers more readable by putting each three digits in one group, from the end toward the start), But suddenly when I entered this: jsfksdjfksdjfkdsjfskjdfkjsfn , I got the error message:"String iterator not decrementable".
Presumably I need to study much more pages of my book for C++ to be able to solve this myself, but for now I'm just being super-curious as a beginner. Why is that error message? Thanks in advance.
When you insert() into your string the iterators to it may get invalidated. In particular all iterators past the insertion point should be considered invalidated in all cases but also all iterators get invalidated if the std::string needs to get more memory: the internal buffer will be replaced by a bigger one, causing all existing iterator (and references and pointers) to string elements to be invalidated.
The easiest fix to the problem is to make sure that the string doesn't need to allocate more memory by reserve()ing enough space ahead of time. Since you add one space for every two characters, making sure that there is space for str1.size() + str1.size() / 2u characters should be sufficient:
str1.reserve(str1.size() + str1.size() / 2u);
for (auto PlaceCounter = str1.end(); PlaceCounter != str1.begin(); --PlaceCounter) {
// ...
}
Note that your algorithm is rather inefficient: it is an O(n2). The operation can be done with O(n) complexity instead. You'd resize the string to the appropriate size right from the start, filling the tail with some default characters and then copy the content moving from the end directly to the appropriate location.
str1.insert(str1.end()-k-i,' ');
This modifies the string the loop is iterating over. Specifically, this inserts something into the string.
With a std::string, much like a std::vector, insertion into a string will (may) invalidate all existing iterators pointing to the string. The first insertion performed by the shown code results in undefined behavior, as soon as the existing, now invalidated, iterators are referenced afterwards.
You will need to either replace your iterators with indexes into the string, or instead of modifying the existing string construct a new string, leaving the original string untouched.
Here is a possible C++ approach to try. From my tool bag, here is how I insert commas into a decimal string (i.e. s is expected to contain digits):
Input: "123456789"
// insert comma's from right (at implied decimal point) to left
std::string digiCommaL(std::string s)
{
// Note: decrementing a uint (such as size_t) will loop-around,
// and not underflow. Be sure to use int ...
int32_t sSize = static_cast<int32_t>(s.size()); // change to int
// ^^^^^-----------_____
if (sSize > 3) vvvvv
for (int32_t indx = (sSize - 3); indx > 0; indx -= 3)
s.insert(static_cast<size_t>(indx), 1, ',');
return(s);
}
Returns: "123,456,789"

Recursive String Transformations

EDIT: I've made the main change of using iterators to keep track of successive positions in the bit and character strings and pass the latter by const ref. Now, when I copy the sample inputs onto themselves multiple times to test the clock, everything finishes within 10 seconds for really long bit and character strings and even up to 50 lines of sample input. But, still when I submit, CodeEval says the process was aborted after 10 seconds. As I mention, they don't share their input so now that "extensions" of the sample input work, I'm not sure how to proceed. Any thoughts on an additional improvement to increase my recursive performance would be greatly appreciated.
NOTE: Memoization was a good suggestion but I could not figure out how to implement it in this case since I'm not sure how to store the bit-to-char correlation in a static look-up table. The only thing I thought of was to convert the bit values to their corresponding integer but that risks integer overflow for long bit strings and seems like it would take too long to compute. Further suggestions for memoization here would be greatly appreciated as well.
This is actually one of the moderate CodeEval challenges. They don't share the sample input or output for moderate challenges but the output "fail error" simply says "aborted after 10 seconds," so my code is getting hung up somewhere.
The assignment is simple enough. You take a filepath as the single command-line argument. Each line of the file will contain a sequence of 0s and 1s and a sequence of As and Bs, separated by a white space. You are to determine whether the binary sequence can be transformed into the letter sequence according to the following two rules:
1) Each 0 can be converted to any non-empty sequence of As (e.g, 'A', 'AA', 'AAA', etc.)
2) Each 1 can be converted to any non-empty sequences of As OR Bs (e.g., 'A', 'AA', etc., or 'B', 'BB', etc) (but not a mixture of the letters)
The constraints are to process up to 50 lines from the file and that the length of the binary sequence is in [1,150] and that of the letter sequence is in [1,1000].
The most obvious starting algorithm is to do this recursively. What I came up with was for each bit, collapse the entire next allowed group of characters first, test the shortened bit and character strings. If it fails, add back one character from the killed character group at a time and call again.
Here is my complete code. I removed cmd-line argument error checking for brevity.
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
using namespace std;
//typedefs
typedef string::const_iterator str_it;
//declarations
//use const ref and iterators to save time on copying and erasing
bool TransformLine(const string & bits, str_it bits_front, const string & chars, str_it chars_front);
int main(int argc, char* argv[])
{
//check there are at least two command line arguments: binary executable and file name
//ignore additional arguments
if(argc < 2)
{
cout << "Invalid command line argument. No input file name provided." << "\n"
<< "Goodybe...";
return -1;
}
//create input stream and open file
ifstream in;
in.open(argv[1], ios::in);
while(!in.is_open())
{
char* name;
cout << "Invalid file name. Please enter file name: ";
cin >> name;
in.open(name, ios::in);
}
//variables
string line_bits, line_chars;
//reserve space up to constraints to reduce resizing time later
line_bits.reserve(150);
line_chars.reserve(1000);
int line = 0;
//loop over lines (<=50 by constraint, ignore the rest)
while((in >> line_bits >> line_chars) && (line < 50))
{
line++;
//impose bit and char constraints
if(line_bits.length() > 150 ||
line_chars.length() > 1000)
continue; //skip this line
(TransformLine(line_bits, line_bits.begin(), line_chars, line_chars.begin()) == true) ? (cout << "Yes\n") : (cout << "No\n");
}
//close file
in.close();
return 0;
}
bool TransformLine(const string & bits, str_it bits_front, const string & chars, str_it chars_front)
{
//using iterators so store current length as local const
//can make these const because they're not altered here
int bits_length = distance(bits_front, bits.end());
int chars_length = distance(chars_front, chars.end());
//check success rule
if(bits_length == 0 && chars_length == 0)
return true;
//Check fail rules:
//1. next bit is 0 but next char is B
//2. bits length is zero (but char is not, by previous if)
//3. char length is zero (but bits length is not, by previous if)
if((*bits_front == '0' && *chars_front == 'B') ||
bits_length == 0 ||
chars_length == 0)
return false;
//we now know that chars_length != 0 => chars_front != chars.end()
//kill a bit and then call recursively with each possible reduction of front char group
bits_length = distance(++bits_front, bits.end());
//current char group tracker
const char curr_char_type = *chars_front; //use const so compiler can optimize
int curr_pos = distance(chars.begin(), chars_front); //position of current front in char string
//since chars are 0-indexed, the following is also length of current char group
//start searching from curr_pos and length is relative to curr_pos so subtract it!!!
int curr_group_length = chars.find_first_not_of(curr_char_type, curr_pos)-curr_pos;
//make sure this isn't the last group!
if(curr_group_length < 0 || curr_group_length > chars_length)
curr_group_length = chars_length; //distance to end is precisely distance(chars_front, chars.end()) = chars_length
//kill the curr_char_group
//if curr_group_length = char_length then this will make chars_front = chars.end()
//and this will mean that chars_length will be 0 on next recurssive call.
chars_front += curr_group_length;
curr_pos = distance(chars.begin(), chars_front);
//call recursively, adding back a char from the current group until 1 less than starting point
int added_back = 0;
while(added_back < curr_group_length)
{
if(TransformLine(bits, bits_front, chars, chars_front))
return true;
//insert back one char from the current group
else
{
added_back++;
chars_front--; //represents adding back one character from the group
}
}
//if here then all recursive checks failed so initial must fail
return false;
}
They give the following test cases, which my code solves correctly:
Sample input:
1| 1010 AAAAABBBBAAAA
2| 00 AAAAAA
3| 01001110 AAAABAAABBBBBBAAAAAAA
4| 1100110 BBAABABBA
Correct output:
1| Yes
2| Yes
3| Yes
4| No
Since a transformation is possible if and only if copies of it are, I tried just copying each binary and letter sequences onto itself various times and seeing how the clock goes. Even for very long bit and character strings and many lines it has finished in under 10 seconds.
My question is: since CodeEval is still saying it is running longer than 10 seconds but they don't share their input, does anyone have any further suggestions to improve the performance of this recursion? Or maybe a totally different approach?
Thank you in advance for your help!
Here's what I found:
Pass by constant reference
Strings and other large data structures should be passed by constant reference.
This allows the compiler to pass a pointer to the original object, rather than making a copy of the data structure.
Call functions once, save result
You are calling bits.length() twice. You should call it once and save the result in a constant variable. This allows you to check the status again without calling the function.
Function calls are expensive for time critical programs.
Use constant variables
If you are not going to modify a variable after assignment, use the const in the declaration:
const char curr_char_type = chars[0];
The const allows compilers to perform higher order optimization and provides safety checks.
Change data structures
Since you are perform inserts maybe in the middle of a string, you should use a different data structure for the characters. The std::string data type may need to reallocate after an insertion AND move the letters further down. Insertion is faster with a std::list<char> because a linked list only swaps pointers. There may be a trade off because a linked list needs to dynamically allocate memory for each character.
Reserve space in your strings
When you create the destination strings, you should use a constructor that preallocates or reserves room for the largest size string. This will prevent the std::string from reallocating. Reallocations are expensive.
Don't erase
Do you really need to erase characters in the string?
By using starting and ending indices, you overwrite existing letters without have to erase the entire string.
Partial erasures are expensive. Complete erasures are not.
For more assistance, post to Code Review at StackExchange.
This is a classic recursion problem. However, a naive implementation of the recursion would lead to an exponential number of re-evaluations of a previously computed function value. Using a simpler example for illustration, compare the runtime of the following two functions for a reasonably large N. Lets not worry about the int overflowing.
int RecursiveFib(int N)
{
if(N<=1)
return 1;
return RecursiveFib(N-1) + RecursiveFib(N-2);
}
int IterativeFib(int N)
{
if(N<=1)
return 1;
int a_0 = 1, a_1 = 1;
for(int i=2;i<=N;i++)
{
int temp = a_1;
a_1 += a_0;
a_0 = temp;
}
return a_1;
}
You would need to follow a similar approach here. There are two common ways of approaching the problem - dynamic programming and memoization. Memoization is the easiest way of modifying your approach. Below is a memoized fibonacci implementation to illustrate how your implementation can be speeded up.
int MemoFib(int N)
{
static vector<int> memo(N, -1);
if(N<=1)
return 1;
int& res = memo[N];
if(res!=-1)
return res;
return res = MemoFib(N-1) + MemoFib(N-2);
}
Your failure message is "Aborted after 10 seconds" -- implying that the program was working fine as far as it went, but it took too long. This is understandable, given that your recursive program takes exponentially more time for longer input strings -- it works fine for the short (2-8 digit) strings, but will take a huge amount of time for 100+ digit strings (which the test allows for). To see how your running time goes up, you should construct yourself some longer test inputs and see how long they take to run. Try things like
0000000011111111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBAAAAAAAA
00000000111111110000000011111111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBAAAAAAAA
and longer. You need to be able to handle up to 150 digits and 1000 letters.
At CodeEval, you can submit a "solution" that just outputs what the input is, and do that to gather their test set. They may have variations so you may wish to submit it a few times to gather more samples. Some of them are too difficult to solve manually though... the ones you can solve manually will also run very quickly at CodeEval too, even with inefficient solutions, so there's that to consider.
Anyway, I did this same problem at CodeEval (using VB of all things), and my solution recursively looked for the "next index" of both A and B depending on what the "current" index is for where I was in a translation (after checking stoppage conditions first thing in the recursive method). I did not use memoization but that might've helped speed it up even more.
PS, I have not run your code, but it does seem curious that the recursive method contains a while loop within which the recursive method is called... since it's already recursive and should therefore encompass every scenario, is that while() loop necessary?

C++ Set Erase Entry Question

I encountered a problem here. I'm using C++ multiset. This is the test file.
Score: 3-1
Ben
Steven
Score: 1-0
Ben
Score: 0-0
Score: 1-1
Cole
Score: 1-2
Ben
I'm using while loop and ifstream (fin1) to read in from the test file above.
multiset<string, less<string> > myset;
while(!fin1.eof())
{
fin1 >> scoreName;
if(scoreName == "Score:")
{
//calculates number of matches played
}
else
{
goalCheck = scoreName.substr(1,1);
if(goalCheck == "-")
{
string lGoal, rGoal;
lGoal = scoreName.substr(0,1);
rGoal = scoreName.substr(2,1);
int leftGoal, rightGoal;
leftGoal = atoi(lGoal.c_str());
rightGoal = atoi(rGoal.c_str());
if(leftGoal > rightGoal) //if team wins
{
//some computations
}
else if(leftGoal < rightGoal) //if team loses
{
//computations
}
else if(leftGoal == rightGoal) //if team draws
{
//computations
}
else
{
myset.insert(myset.begin(), scoreName);
}
}
}
I'm inserting all names into myset (regardless of wins/loses/draws) in my last else statement. But I only require the names of those matches who won/draw.
Those names whose matches lost will not be included in myset. In the test file above, there's only one match that lost (1-2) and I wanted to remove "Ben". How can I do that?
I tried to use myset.erase(), but I'm not sure how to get it point to Ben and remove it from myset.
Any help is much appreciated.
Thanks.
If I understand what you're trying to do, I think it would be easier to remember whether the team had won, drawn or lost when you read the "Score" line and only insert the following lines (ie. "Ben") if the team hasn't lost. Inserting everyone and then erasing ones you didn't want seems overcomplicated to me :)
For reference: If you do need to erase, you would use the find() member to locate an entry matching a given key, and then erase() on the returned iterator to delete it (after checking that find() didn't return an iterator equal to end(), which means the item wasn't found).
Also, you shouldn't pass begin() to insert(). The iterator is a hint as to where the map might insert the item; in practice that's rarely useful. There is an overload of that function which takes only one argument, the item to insert.
Create a Score class. Add it a non-member operator>>() so you can parse it easily. Then it will be easy for you decide whether to insert a Score object into the set or not:
if( scr.gained - scr.lost >= 0 )
myset.insert(myset.begin(), scr);

trying to sort a simple string in c++

#include "stdio.h"
#include "conio.h"
#include <iostream>
using namespace std;
int main (void)
{
char my_char[] = "happy birthday";
int i;
bool j=false;
char my_char_temp[1];
do
{
for (i=0;i<sizeof(my_char)-2;i++)
{
j=false;
if (my_char[i+1] < my_char[i])
{
my_char_temp[0]=my_char[i+1];
my_char[i+1] = my_char[i];
my_char[i] = my_char_temp[0];
j=true;
}
}
}while (j);
cout << my_char;
}
What am I doing wrong?
I'm just trying to sort the letters within the char.
The output I get is completely wrong.
You want to use strlen() rather than sizeof.
You are resetting j to false each and every time you compare two characters.
This means that, if you swap two characters, and you are NOT at the end of your array, you will forget that you have swapped them.
Move the j=false; from inside the for-loop to just inside the do-loop.
And you owe me a bottle of Jack for saving your ass on a homework assignment on Sunday afternoon.
I don't know what are you trying to implement with your sizeof(...) - 2 and etc, but what you probably want to get can be done this way:
#include <iostream>
#include <algorithm>
int main() {
std::string s("happy birthday");
std::sort(s.begin(), s.end());
}
Consider what happens inside this loop:
for (i=0;i<sizeof(my_char)-2;i++)
If you find a pair of values to swap, setting j to true, you'll continue iterating through that loop, and set j back to false on the next iteration. As a result, the program is going to exit as soon as the last two characters in the string are in sorted order, regardless of whether the rest of the string is sorted.
Instead, as soon as you find a pair of characters to swap, you want to start over again at i=0. The simplest way to do that is add a break; statement after your j = true line. With that fix, this works correctly.
Alternately, you could move the initial j = false line outside the loop, which would solve the problem in a slightly different way.
You are actually very close. The only problem is that
j=false;
needs to be in the outer loop. As is, j is cleared every time the inner loop executes.
With this fix, your program works fine for me.
Stylistic errors, however, are another story.
I could be mistaken but it looks like you're trying to do a bubble sort?
And it's i < sizeof(my_char)-2 because he's using a 0-based, null terminated string, and he doesn't want to sort the null terminator.
Try just repeating the condition of the inner loop, using j instead of i, and see if that works? Note that this has a run time of O(n^2) and you can get sorts down much much faster than that if you need to. Alternately you can move the boolean out of the for and into the do loop.
for (i=0;i < sizeof(my_char)-2;i++)
for (i=0;i<sizeof(my_char)-2;i++)
{
if (my_char[i+1] < my_char[i])
{
my_char_temp[0]=my_char[i+1];
my_char[i+1] = my_char[i];
my_char[i] = my_char_temp[0];
}
}