I wrote a function to check whether a word is palindrome or not but "unexpectedly", that function failed quite badly, here it is:
bool isPalindrome (const string& s){
string reverse = "";
string original = s;
for (string_sz i = 0; i != original.size(); ++i){
reverse += original.back();
original.pop_back();
}
if (reverse == original)
return true;
else
return false;
}
It gives me "string iterator offset out of range error" when you pass in a string with only one character and returns true even if we pass in an empty string (although I know its because of the intialisation of the reverse variable) and also when you pass in an unassigned string for example:
string input;
isPalindrome(input);
Later, I found a better function which works as you would expect:
bool found(const string& s)
{
bool found = true;
for (string::const_iterator i = s.begin(), j = s.end() - 1; i < j; ++i, --j) {
if (*i != *j)
found = false;
}
return found;
}
Unlike the first function, this function correctly fails when you give it an unassigned string variable or an empty string and works for single characters and such...
So, good people of stackoverflow please point out to me why the first function is so bad...
Thank You.
for (string_sz i = 0; i != original.size(); ++i) {
reverse += original.back();
original.pop_back();
}
original.size() changes as you pop elements off the back. Effectively, you keep incrementing i and decrementing original.size(); they may never be equal.
if (reverse == original)
This will never be true since you've just removed all of the elements from original and added them in reverse order to reverse. original will always be empty at this point.
You're found function could very well rely on the STL std::compare function and on the begin()/end() rbegin()/rend() functions of the string. and could be a one line function :
return std::equal(s.begin(), s.end(), s.rbegin());
The std::equal() function compares two ranges of the same length.
The begin()/end() functions provide forward iterators while rbegin() provides a reverse iterator, ie an iterator that starts at the end of the string and goes to the beginning.
This is probably not what you want, but reverse is already implemented as an algorithm in STL:
bool isPalindrome( const std::string & str )
{
std::string rev( str );
std::reverse( rev.begin(), rev.end() );
return str==rev;
}
As #James McNellis points out, this can be further condensed (without needing any algorithm) by constructing the reversed string directly with reverse iterators on the original string:
bool isPalindrome( const std::string & str )
{
return str == std::string( str.rbegin(), str.rend() );
}
The loop that purports to reverse the string doesn't in fact do so. As you're removing items from the list, you're also incrementing i. In some cases I imagine it's possible for i to skip past the current size and iterate forever.
Instead of your loop, you can use reverse:
std::reverse(original.begin(), original.end());
And then do the rest of the work. It's up to your requirements if an empty string is a palindrome or not.
Your solutions are far too complicated ;)
bool is_palindrome(std::string const& s) {
if (s.empty()) return false; // if this is required.
return !lexicographical_compare(s.begin(), s.end(), s.rbegin(), s.rend());
}
Edit: Or, as Etienne noted, just use std::equal ...
bool is_palindrome(std::string const& s) {
if (s.empty()) return false; // if this is required.
return equal(s.begin(), s.end(), s.rbegin());
}
Related
I am trying to validate a single-line input string in C++11 to see if it contains any leading / trailing whitespaces. My code now looks like this:
bool is_valid(const std::string& s) {
auto start = s.begin();
auto end = s.end();
if (std::isspace(*start) || std::isspace(*end)) {
return false;
}
return true;
}
int main() {
std::string name{};
std::getline(std::cin, name);
if (!is_valid(name)) {
std::cout << "Invalid!";
}
return 0;
}
But now the program can only detect leading whitespaces. For example, for John it would print Invalid! but for Mary it would classify it as valid input, which is not. Does anyone know what's wrong with my program?
A simple test for std::string::front() and std::string::back() could have been done after testing for the empty string:
bool is_valid(const std::string& s)
{
return s.empty() ||
(!std::isspace(static_cast<unsigned char>(s.front())) &&
!std::isspace(static_cast<unsigned char>(s.back())));
}
The end iterator does not point to an element in the container. It points one past the last element. You may not dereference the end iterator. For a std::string you can use it's operator[]:
char last_char = s[s.size()-1];
advance the begin iterator:
auto it = s.begin() + s.size()-1;
char last_char = *it;
or decrement the end iterator:
auto it = s.end() -1;
char last_char = *it;
Other alternatives are back() or using the reverse iterator rbegin().
Note that they all require s.size() != 0. For an empty string s.begin() == s.end(). You should check that first in the function and return true for that case.
s.end() is one pass the end of the string just like any other containers in C++, so accessing it invokes undefined behavior. You need to use std::prev(s.end()) instead (which is valid only the string contains at least 1 character though, so you need to check the string length first)
.end is used to get an iterator to past the last element. You can use std::string::rbegin to get the last element.
auto end = s.rbegin();
NB: std::string::starts_with and std::string::ends_with are available from C++20.
Yes, .end() is to the past-the-end element. Then why not using .back() instead?
bool is_valid(std::string const& str) {
return str.empty() || !(std::isspace(str.front()) || std::isspace(str.back()));
}
I'd like to know the fastest way for reading the last line in a std::string object.
Technically, the string after the last occurrence of \n in the fastest possible way?
This can be done using just string::find_last_of and string::substr like so
std::string get_last_line(const std::string &str)
{
auto position = str.find_last_of('\n');
if (position == std::string::npos)
return str;
else
return str.substr(position + 1);
}
see: example
I would probably use std::string::rfind and std::string::substr combined with guaranteed std::string::npos wrap around to be succinct:
inline std::string last_line_of(std::string const& s)
{
return s.substr(s.rfind('\n') + 1);
}
If s.rfind('\n') doesn't find anything it returns std::string::npos. The C++ standard says std::string::npos + 1 == 0. And returning s.substr(0) is always safe.
If s.rfind('\n') does find something then you want the substring starting from the next character. Again returning s.substr(s.size()) is safe according to the standard.
NOTE: In C++17 this method will benefit from guaranteed return value optimization so it should be super efficient.
I thought of a way that reads the string inversely (backwards) while storing what it reads
std::string get_last_line(const std::string &str)
{
size_t l = str.length();
std::string last_line_reversed, last_line;
for (--l; l > 0; --l)
{
char c = str.at(l);
if (c == '\n')
break;
last_line_reversed += c;
}
l = last_line_reversed.length();
size_t i = 0, y = l;
for (; i < l; ++i)
last_line += last_line_reversed[--y];
return last_line;
}
until it counters a '\n' character then reverse the stored string back and return it. If the target string is big and has a lot of new lines, this function would be very efficient.
I am trying to check if char exists in vector of chars, and if so, to get it`s number. I have done the first part (check if char exists):
char letter(a);
string word;
vector<char>vWord(word.begin(), word.end());
if(find(vWord.begin(), vWord.end(), letter) != vWord.end()){}
But I have no idea how to get the position. Any help is appreciated.
Save the iterator and do some math on it:
vector<char>::iterator itr = find(vWord.begin(), vWord.end(), letter);
if(itr != vWord.end())
{
int index = itr - vWord.begin();
}
However do note that std::string already has a find method.
You're almost there. You already have an iterator pointing to that character (returned by find), so you can use std::distance to find the distance:
char letter(a);
string word;
vector<char>vWord(word.begin(), word.end());
auto it = find(vWord.begin(), vWord.end(), letter);
if (it != vWord.end())
{
size_t index = std::distance(vWord.begin(), it);
}
For random-access iterators (such as those used by std::vector), std::distance(a, b) is a constant-time operation and is implemented by doing b - a.
Side note: you can do std::find and iterator operations on std::string directly; it's a perfectly fine container in its own right.
I am trying to compare two std::strings, and decide if string A is the same as string B, but with the insertion or deletion of a single character.
Otherwise it returns false.
For example: "start" and "strt" or "ad" and "add"
Currently:
if(((sizeA - sizeB) != 1)
&& ((sizeB - sizeA) != 1))
{
return false;
}
if(sizeA < sizeB)
{
for(int i = 0; i < sizeA; ++i)
{
if(stringA[i] != stringB[i])
{
if(stringA.substr(i)
== stringB.substr(i + 1))
{
return true;
}
else return false;
}
}
} //with another loop that runs only if stringA is larger than stringB
This works flawlessly, but gprof tells me that this function is being bogged down.
I tried converting the for loop to use iterators to access the chars, but this doubled my run time.
Ive narrowed it down to my use of std::string.substr( ) because it is constructing new strings each time stringA and stringB differ in size by 1.
When the first character differs, I need a more efficient way to check if I were to delete that character, would the two strings then be equal?
It seems, once it is known whether there is a one character difference the comparison can be done more effective with a single pass over the string: find the location of the difference, skip the character, and see if the tail is the same. To that end it is obviously necessary to know which one is the smaller string but that's trivial to determine:
bool oneCharDiff(std::string const& shorter, std::string const& longer) {
if (shorter.size() + 1u != longer.size() {
return false;
}
typedef std::string::const_iterator const_iterator;
std::pair<const_iterator, const_iterator> p
= std::mismatch(shorter.begin(), shorter.end(), longer.begin());
return std::equal(p.first, shorter.end(), p.second + 1);
}
bool atMostOneCharDiff(std::string const& s0, std::string const& s1) {
if (s0.size() < s1.size()) {
return oneCharDiff(s0, s1);
else if (s1.size() < s0.size()) {
return oneCharDiff(s1, s0);
}
else {
return s0 == s1;
}
}
Try:
if (stringA.compare(i, stringA.npos, stringB, i+1, stringB.npos) == 0) {
/* the strings are equal */
}
In this write-up, that's version (3) of std::basic_string::compare.
If your compiler supports it it may be worth checking out the new ISO/IEC TS 19568:xxxx Technical Specification string_view class.
It provides an immutable view of a string through references without copying the string itself so it promises to be much more efficient when dealing with substrings.
#include <experimental/string_view>
using std::experimental::string_view;
bool func(string_view svA, string_view svB)
{
// ... stuff
if(svA.size() < svB.size())
{
for(int i = 0; i < svA.size(); ++i)
{
if(svA[i] != svB[i])
{
if(svA.substr(i)
== svB.substr(i + 1))
{
return true;
}
else return false;
}
}
}
// ... stuff
return false;
}
As you can see it works pretty much like a drop-in replacement for std::string (or const char* etc...). Simply pass your normal std::string objects as arguments to the function and the string_view parameters will initialize from the passed in strings.
(C++)
Given myString, I want to check if myString contains substring. Here's what I have so far, but it only returns true if the string begins with the substring.
bool find(string myString, string substring)
{
if(mystring.length() < substring.length())
{
return false;
}
if(mystring == substring)
{
return true;
}
for(int i = 0; i < substring.length() - 1 ; ++i)
{
if(mystring.at(i) == substring.at(i))
{
continue;
}
else
{
string string2 = mystring.substr(1, mystring.length() - 1);
return find(string2, substring);
}
return true;
}
return false;
}
What is wrong with this function?
Check this function, it based on your code, with removal of extra code and fix of the errors.
I also changed the signature to get const reference to improve the efficiency.
bool find(const string& myString, const string& substring)
{
if(myString.length() < substring.length()){
return false;
}
else if(myString.substr(0,substring.size()) == substring){
return true;
}
else if (myString.length() > substring.length()){
return find(myString.substr(1), substring);
}
else{
return false;
}
}
First of all the function can be written simpler. For example
bool find( const std::string &myString, const std::string &subString )
{
return
( myString.substr( 0, subString.size() ) == subString ) ||
( subString.size() < myString.size() && find( myString.substr( 1 ), subString ) );
}
Here is a demonstrative program
#include <iostream>
#include <iomanip>
#include <string>
bool find( const std::string &myString, const std::string &subString )
{
return
( myString.substr( 0, subString.size() ) == subString ) ||
( subString.size() < myString.size() && find( myString.substr( 1 ), subString ) );
}
int main()
{
std::cout << std::boolalpha << find( "Hello World", "World" ) << std::endl;
std::cout << std::boolalpha << find( "Hello C", "C++" ) << std::endl;
}
The program output is
true
false
As for your function then it will return true only in the case when the both string have the same length and are equal each other
if(myString == substring){
return true;
}
And in case when myString.length() > substring.length() the function returns nothing
else if (myString.length() > substring.length()){
int start = 1;
int end = (int) myString.length() - 1;
string string2 = myString.substr(start, end);
find(string2, substring);
}
I think you mean
return find(string2, substring);
in this code snippet.
EDIT: I see that you changed the code of the function in your post. But in any case this code snippet
for(int i = 0; i < substring.length() - 1 ; ++i)
{
if(mystring.at(i) == substring.at(i))
{
continue;
}
else
{
string string2 = mystring.substr(1, mystring.length() - 1);
return find(string2, substring);
}
return true;
}
makes no sense.
You're missing a return before the recursive call to find. As it stands it falls through to the return false at the end.
Also, if (mystring == substring) should be checking if mystring starts with substring, not exact equality.
First, this is expensive because of the memory copies in substr.
Second, you havent checked for substring length > 0.
Third, the "else if" check for mystring.length > 0 is redundant if you have done the other checks (including substring length > 0).
Now to your core logic. In the recursion your start is never moving, so you are tied to the beginning. What you need to do is start with position 1, and increment start at every recursion, and also extract using substr the substring from "start" to "start + substring.length". That way you start from the beginning, keep moving forward, and check the correct length. You could also start from the end (as you have) and move back, what you would have to do there is: find sart position (end position minus length of substring), and check that the start position is not less than zero before calling the function recursively.
You're just removing the leftmost characters of myString and then comparing the rest to your substring. Obviously, this is not going to work in a general case, when your substring is somewhere in the middle of myString.
On each iteration try comparing not the whole myString, but rather the first substring.size() characters of it. This should fix your issue.
Here's what I have so far, but it only returns true if the string
begins with the substring.
It also fails for find("foo", "f").
To see why, add some test output to the function:
bool find(string myString, string substring)
{
std::cout << myString << ", " << substring << "\n";
// ...
}
It will print:
foo, f
oo, f
o, f
You see why this cannot work? You just keep removing the first character, until only the last character is compared with the substring to be found.
But it fails even for find("foo", "o"):
foo, o
oo, o
o, o
That's because of this line:
find(string2, substring);
You don't return the result of the recursive call.
All things considered, I think you just have the wrong algorithm here. It simply cannot work the way you have written the code.
A few other observations:
int start = 1;
int end = (int) myString.length() - 1;
That's not good style. For historical reasons, a std::string's size is unsigned, and you are using a C-style cast where static_cast should be preferred. You should just use std::string::size_type here, because it's just an internal piece of implementation code and you gain nothing from casting to int.
string string2 = myString.substr(start, end);
The second argument of substr defines the length of the substring, not the index of the last character. end sounds like you use the value as the index of the last character. Have a look at http://en.cppreference.com/w/cpp/string/basic_string/substr.