So while working through a course on Udemy over C++ one of the challenges was to check a string to see whether it was a palindrome or not. I completed the task successfully but went about it a different way than the instructor. I understand there are a multitude of ways to complete a task but I am wondering which is more efficient and why? It may seem stupid to be wondering about this while reteaching myself coding but I feel this is something I should be keeping in mind.
//Instructors code//
# include<iostream>
using namespace std;
/*program for reverse a string and check a string is a palidrome
*/
int main()
{
string str="MADAM";
string rev="";
int len=(int)str.length();
rev.resize(len);
for(int i=0, j=len-1; i<len; i++, j--)
{
rev[i]=str[j];
}
rev[len]='\0';
if(str.compare(rev)==0)
cout<<"palindrome"<<endl;
else
cout<<"not a pallindrome"<<endl;
return 0;
}
My Approach
#include <iostream>
using namespace std;
int main(){
string str1="test";
// cout << "Enter a string to check if it is a Palindrome: ";
// getline(cin,str1);
string str2;
string::reverse_iterator it;
for(it=str1.rbegin(); it!= str1.rend(); it++)
{
str2.push_back(*it);
}
if(!str1.compare(str2))
cout << "\nPalindrome";
else
cout << "\nNot a Palindrome";
return 0;
}
Thank you in advance.
In theory the code from your instructor is more efficient, but both examples have issues.
With your instructors code the main issue is the use of
int len=(int)str.length();
In this example, it is okay because we know the size of the string will fit in a int, but if you were getting a string from an outside source, this could be a problem. A std::string using an unsigned integer type to store the size of the string and that means you can have a string who's size is larger then what can fit in an int. If that were to happen, then code is not going to work correctly.
With your code you a avoid all that, which is great, but you also leave some performance on the table. In theory your code of
for(it=str1.rbegin(); it!= str1.rend(); it++)
{
str2.push_back(*it);
}
is going to cause str2 to have multiple buffer allocations and copies from the old buffer to the new buffer as it grows. This is a lot of extra work that you don't need to do since you already know how much space you need to allocate. Having
str2.reserve(str1.size() + 1);
before the loop pre-allocates all the space you need so you don't have those potential performance hits.
Then we come to the fact that both of your examples are using a second string. You don't need another string to check for a palindrome. What you can do is just check and see if the first and last characters are the same, and if they are move on to the first+1 and last-1 character and so on until you reach the middle or they don't match. You can do that using a construct like
bool is_palindrome = true;
for (auto start = str.begin(), end = str.end() - 1;
start < end && is_palindrome;
++start, --end)
{
if (*start != *end)
is_palindrom = false
}
if (is_palindrome)
std::cout << "palindrome\n";
else
std::cout << "not a pallindrome\n";
The simplest and most efficient way (no copying required) would be something like this:
inline bool is_palindrome(const std::string& u) {
return std::equal(u.begin(), std::next(u.begin(), u.length() / 2), u.rbegin());
}
I would say that both are almost the same, but as mentioned in the comments, the line:
str2.push_back(*it);
Is actually very inefficient, since std::string may copy the existing string to a new location in the memory, and then append the next char to the string, which is wasteful.
But I am wondering, why to create the copy in the first place?
It is very simple to run both from start to end, and from end to start to check it out, meaning:
bool is_polindrom(const std::string& str)
{
for (std::size_t idx = 0, len = str.length(); idx < len / 2; ++idx)
{
if (str[idx] != str[len - 1 - idx])
{
return false;
}
}
return true;
}
Running the code with:
int main()
{
const std::string right1 = "MADAM";
const std::string right2 = "MAAM";
const std::string wrong1 = "MADAAM";
const std::string wrong2 = "MEDAM";
std::cout << "MADAM result is: " << is_polindrom(right1) << std::endl;
std::cout << "MAAM result is: " << is_polindrom(right2) << std::endl;
std::cout << "MADAAM result is: " << is_polindrom(wrong1) << std::endl;
std::cout << "MEDAM result is: " << is_polindrom(wrong2) << std::endl;
}
Will yield:
MADAM result is: 1
MAAM result is: 1
MADAAM result is: 0
MEDAM result is: 0
You don't need extra memory in this case, since it is possible to iterate over a string from the end to the beginning, and you need to run on it exactly once (and notice that I stop when idx >= len / 2 since you don't really need to check each letter twice!).
Related
This is my first time asking something on stackoverflow, so I'm sorry if I fail in any aspect of building the topic etc...
So I'm a newbie at C++, I'm still at the beginning. I'm using a guide someone recommended me, and I'm stuck in a exercise which is about char and strings.
It's the following: They ask me to create a function that says the number of times that a certain word was repeated on a string.
I'll leave my code below for someone who can help me, if possible dont give me an obvious response like the code and then I just copy paste it. If you can just give me some hints on how to do it, I want to try to solve it on my own. Have a good night everyone.
#include <iostream>
#include <string.h>
#define MAX 50
using namespace std;
int times_occ(string s, string k) {
int count = 0, i = 0;
char word[sizeof(s)];
// while (s[i] == k[i])
// {
// i++;
// if (s[i] == '\0')
// {
// break;
// }
// }
for (i = 0; i <= sizeof(s); i++) {
if (s[i] == ' ' || s[i] == '\0') {
break;
}
word[i] = s[i];
}
word[i] = '\0';
for (i = 0; i <= sizeof(k); i++) {
if (word) {
if (k[i] == word[a]) {
a++;
count++;
}
}
}
cout << word << endl;
cout << count << endl; // this was supposed to count the number of times
// certain word was said in a string.
return count;
}
int main() {
char phrase[MAX];
char phrase1[MAX];
cin.getline(phrase, MAX);
cin.getline(phrase, MAX);
times_occ(phrase, phrase1);
}
Okay, first of all, the way you've used sizeof isn't really valid.
sizeof won't tell you the length of a string. For that, you want std::string::size() instead.
In this case, std::string is an object of some class, and sizeof will tell you the size of an object of that class. Every object of that type will yield the same size, regardless of the length of the string.
For example, consider code like this:
std::string foo("123456789");
std::string bar("12345");
std::cout << sizeof(foo) << "\t" << foo.size() << "\n";
std::cout << sizeof(bar) << "\t" << bar.size() << "\n";
When I run this, I get output like this:
8 9
8 5
So on this implementation, sizeof(string) is always 8, but some_string.size() tells us the actual length of the string.
So, that should at least be enough to get you started moving in a useful direction.
As #JerryCoffin mentioned, your word array has an invalid size. But - I want to make a more fundamental point:
Your code has two loops and a bunch of variables with arbitrary names. How should I know what's the difference between s and k? I even get k and i mixed up in the sense of forgetting that k is a string, not an integer. That kind of code difficult to read, and to debug. And we are a bit lazy and don't like debugging other people's code...
I suggest that you:
Have a very clear idea what your loops do, or what the different parts of your function do.
Create small self-contained functions - no more than one loop each please! - for each of those parts.
Use meaningful names for each function's parameters and for the local variables.
And then, if your program doesn't work - try debugging one function at a time.
I am passing a string to my function, and the function is supposed to use that string to put individual chars in a stack. Then the stack is supposed to spit it back out (Since it's a stack it should be reversed). For example if my string is hello, it should print "olleh". But instead I'm getting ooooo. I think it has something to do with the fact that I'm setting ch equal to a different character every time but I'm not sure how to input those character in a different way.
void Stack::function2reverse(string myString) {
int countItIt = 0;
int sizeOfString = myString.size();
char Ch ;
for (int i= 0; i< sizeOfString; x++)
{
Ch = myString[x];
stack.push(Ch);
countIt ++;
}
while (countIt != 0)
{
cout << Ch;
stack.pop();
countIt --;
}
}
cout << Ch; - you print the same character every time (the last one entered, so 'o').
Instead, print the top character in the stack: std::cout << stack.top().
std::stack keeps track of its own size, so you don't need to worry about that either. Then you can replace your print loop with:
while (!stack.empty()) {
std::cout << stack.top();
stack.pop();
}
And of course, the Standard Library provides a std::reverse function anyway, so if this was not just an exercise in learning about std::stack, you could use that (and I can think of several other things to do as well, depending on exactly what you are trying to achieve):
std::string s = "hello";
std::reverse(std::begin(s), std::end(s));
// s now contains "olleh"
You may also want to read up on why using namespace std; is a bad practice.
I just needed to reverse a string, so I declared a new string variable and iteratively copied the elements. Now i want to print the reversed string through cout << reversed; but this is printing nothing. I can print it through a for loop through reverse[i] until the size but is there any better way?
#include <iostream>
#include <string>
using namespace std;
int main()
{
string original = "hello";
string reverse;
int i, j = 0, size = original.length();
for (i = size - 1; i >= 0; i--) // original's last as reversed's first
{
reverse[j] = original[i];
j++;
}
reverse[j] = '\0'; //last value as null
cout << "original string = " << original << endl;
cout << "reversed string = " << reverse << endl;
system("pause");
return 0;
}
A better solution is to use std::reverse:
std::string original = "whatever";
std::string rev = original;
std::reverse(rev.begin(), rev.end());
You can use reverse iterators to instantiate the reversed string from the original:
string original = "hello";
string reverse(original.rbegin(), original.rend());
Then
std::cout << "reversed string = " << reverse << std::endl;
Note: avoid using namespace std;. There is an algorithm called std::reverse, whose name you could be inadvertently pulling into the global namespace. And you do have a variable with that name.
See this working demo.
The major problem with your code is that reverse is empty, so using any index leads to you indexing out of bounds and undefined behavior.
As a side-note, you don't have to terminate std::string objects, they are automatically terminated.
you should notice that string reverse doesn't have a size , so when you do something like this
for (i = size - 1; i >= 0; i--) // original's last as reversed's first
{
reverse[j] = original[i];
j++;
}
Here, you are accessing an index that is not found in string reverse,as it's size is 0 . You should take care of something like that.
There are plenty of answers that your received about this question , I just wanted to make you notice this mistake
instead , you can make
reverse+=original[i];
You don't even need to create a new string to hold the reversed string. Just iterator over the original string.
void print_reversed(const std::string& str)
{
std::copy(str.crbegin(), str.crend(), std::ostream_iterator<char>(std::cout));
}
Please tell me why my code to reverse the input string is giving me various errors.
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
void ReverseString(string &aString);
int main(){
string info;
cout << "What's your string?" << endl;
getline(cin, info);
ReverseString(info);
cout << ReverseString(string info) << " compare with: " << info << endl;
system("pause");
return 0;
}
void ReverseString(string &aString){
for(int i = 0; i < aString.length(); i++)
{
string temp = 0; // initialize temporary string
temp = temp + aString.at(aString.length() - 1 - i); // hold temporary string
if(i => aString.length()) /*assign temp string to aString when all chars are processed*/
{
temp = &aString;
}
}
}
Hi you could simplify your code a lot by using the STL
for example:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
std::string str = "Hello World";
cout << str << endl;
std::reverse(str.begin() , str.end());
cout << str << endl;
return 0;
}
let me know if this is not suitable to your needs as theres a few other ways to do it too.
Without STL:
There are some corrections/changes to your code required, which I have supplied below. However you may want to look at some documentation on referencing variables to get an idea of how it works, such as:
http://www.cprogramming.com/tutorial/references.html
http://www.thegeekstuff.com/2013/05/cpp-reference-variable/
http://en.wikipedia.org/wiki/Reference_(C++)
What is a reference variable in C++?
http://www.tutorialspoint.com/cplusplus/cpp_references.htm
Correct reference and pointer use is a major part of C++ and allows for some of the most powerful functionality in the language, provided it is used correctly, or major headaches and mental scarring if used incorrectly, so it is worth, even essential, to have a firm grasp of them.
And even then expect the odd misuse to crop up every-so-often. :)
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
void ReverseString(string &aString);
int main(){
string info;
cout << "What's your string?" << endl;
getline(cin, info);
cout << info << " compare with: ";
ReverseString(info);
cout << info << endl;
system("pause");
return 0;
}
void ReverseString(string &aString)
{
int len = aString.length();
string temp = aString;// initialize temporary string
aString ="";
for(int i = 0; i < len; i++)
{
aString += temp[len - (1+ i)]; // assigns the reversed value to the referenced string
}
}
Just noticed the quote below from #zac-howland : so true, I have however left the code in as an illustrative piece. Provided some reading is done on this as well as plenty of experimentation I hope NewProgrammer will get the information and skill-set he needs to go forward.
#include<iostream>
#include<string>
#include <algorithm>
using namespace std;
string info;
string rvrs(string &str)
{
std::reverse(str.begin(),str.end());
return str;
}
int main()
{
cout<<"What is your string :: ";
getline(cin,info);
cout<<rvrs(info);
cout<<endl;
return 0;
}
You have a few syntactical errors in addition to your logical ones:
cout << ReverseString(string info) << " compare with: " << info << endl;
ReverseString(string info) will pass in an empty string to your ReverseString function (if it even compiles - which looks like it should not since you have 2 info's in the same scope). What you wanted is:
cout << ReverseString(info) << " compare with: " << info << endl;
In your reverse function, you only need to go to length() / 2.
Since you are passing by reference, changes you make to the string within the function will be reflected in the object you passed into it. That is, the original info will be reversed. If you want it to operate on a copy, you need to pass it by copy, not by reference.
Finally, cout << ReverseString(info) is not useful (if it even compiles) as ReverseString returns a void. You should have it return a string (the reversed string).
You have a number of problems.
string temp = 0; // initialize temporary string
It doesn't really make sense to initialize a string to 0. Just string temp; would be fine here.
temp = temp + aString.at(aString.length() - 1 - i); // hold temporary string
That's not quite how I'd do things, but I guess it should work.
if(i => aString.length())
This condition doesn't seem to make sense. Your loop is defined to iterate with i going from 0 to the length of the string -1, so it can never be greater than or equal to the string length.
/*assign temp string to aString when all chars are processed*/
{
temp = &aString;
}
Here the code doesn't match the comment. The comment says you're going to assign to aString, but the code assigns something to temp. The comment is probably closer to what you really want. But you still need to fix the condition, and probably want to do this after the loop has finished executing. So in pseudo-code, you'd end up with something like:
for (all characters in the string)
add the next character in the string to the end of temp
assign temp back to the original string
I am looking for some quick tips on a homework assignment. We are given a few problems and have to write two quick programs on how to solve the problems with one each of iteration and recursion. I'm sure this is easier than I think, but I am getting easily confused over the two. By no means do I want anyone to fully solve the problems for me, I won't learn anything! But if you could look at what I have so far and let me know if I am heading in the right direction. Also, the code does not need to compile, our professor wants us to have a general idea of the differences of iteration vs. recursion.
Problem: check a string to see if it is a palindrome.
My solution- I think it is the iterative solution:
bool iterative_palindrome (const string& str) {
string line, result;
stack <char> stack_input;
//user enters string, program takes it
cout << "Enter string: " << endl;
while (getline (cin, line) && (line != "")) {
//push string into stack
for (size_t i = 0; i < line.size(); i++) {
stack_input.push(line[i]);
//create reverse of original string
while (!stack_input.empty()) {
result += stack_input.top();
stack_input.pop();
return result;
}
//check for palindrome, empty string
if (line == result || line = "0" || line.empty()) {
return true;
cout << line << " is a palindrome!" << endl;
} else {
return false;
cout << line << " is NOT a palindrome." << endl;
cout << "Enter new string: " << endl;
}
}
}
}
I remind everyone, I am pretty new to this stuff. I've read a few things already, but am still having a hard time wrapping my head around this.
Here's the general idea:
Iterative:
Initialize two pointers one pointer to the start and end of the string.
Compare the characters pointed, if different -> not palindrome.
Increase the start pointer and decrease the end pointer.
Repeat until start pointer >= end pointer.
Recursive (more difficult than iterative in this case):
End condition: A string of length zero or one is a palindrome.
A string is a palindrome if the first and last characters are the same and if the string without the first and last characters is a palindrome.
You can implement this recursive algorithm more efficiently by passing pointers to the first and last character in the string instead of copying the string between recursions.
Hope this helps :-)
I figure writing code is the best way to explain the two approaches. Is this code understandable?
bool iterative_palindrome(const string& str) {
int size = str.size();
for (int i=0; i<str.size()/2; i++) {
if (str[i] != str[size-i-1])
return false;
}
return true;
}
You call this like recursive_palindrome(str, 0).
bool recursive_palindrome(const string& str, int index) {
int size = str.size();
if (index >= size/2)
return true;
if (str[index] == str[size-index-1])
recursive_palindrome(str, index+1);
else
return false;
}