what is wrong in my logic reversing vowels in a string? - c++

I tried solving a problem in leetcode
which asks the programmer to reverse the vowels in the given string.
When I wrote my code in C, it ran fine and passed all the test cases.
I tried writing the same code in C++ but for a particular test case, it failed.
bool isVowel(char a)
{
if(a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u')
return true;
if(a == 'A' || a == 'E' || a == 'I' || a == 'O' || a == 'U')
return true;
return false;
}
class Solution {
public:
string reverseVowels(string s) {
int i, j, k;
int len = s.length();
j = s.length() - 1;
i = 0;
k = 0;
string result;
//char result[len];
if (j < 0)
return s;
while(j >= 0) {
if (isVowel(s[j])) {
result[k] = s[j];
k++;
}
j--;
}
k = 0;
j = s.length() - 1;
while (i <= j) {
if(isVowel(s[i])) {
s[i] = result[k];
k++;
}
i++;
}
return s;
}
};
For some reason, when the input is "A new order began, a more Roman age bred Rowena." there is an error message AddressSanitizer: stack-buffer-overflow on address 0x7ffd4a543ab0 at pc 0x000000405efb.
When I tried to debug, I found that, the first while loop gets infinite. But when I replace the string result to char result[len], my code is working fine.
What is wrong in my approach?
Thanks
hago

You may not use the subscript operator for an empty string to change its value.
So your program has undefined behavior.
Pay attention to that in any case you are not reversing vowels in a string. You are trying to create a new string with reversed vowels from a given string. But this is not the same thing.
I can suggest the following Solution.:)
#include <iostream>
#include <string>
#include <cstring>
#include <cctype>
class Solution final
{
private:
static bool isVowel( char c )
{
const char *vowels = "AEIOU";
return std::strchr( vowels, std::toupper( static_cast<unsigned char>( c ) ) );
}
public:
static std::string & reverseVowels( std::string &s )
{
auto first = std::begin( s ), last = std::end( s );
do
{
while ( first != last && !isVowel( *first ) ) ++first;
if ( first != last )
{
while ( --last != first && !isVowel( *last ) );
}
if ( first != last ) std::iter_swap( first++, last );
} while ( first != last );
return s;
}
};
int main()
{
std::string s( "I am trying to write a program in C++" );
std::cout << s << '\n';
std::cout << Solution::reverseVowels( s ) << '\n';
return 0;
}
The program output is
I am trying to write a program in C++
i am tryong ta wreti o prigram In C++
Pay into account that the letter 'y' is not included in the set of vowels.

Your solution is correct but with a simple mistake.
When you declare string result; then this variable is declared with 0 size. So whenever you try to place character at some position (i.e result[0], result[1], ...) it finds that there is no allocated memory for this variable. So it throws error.
In stead of placing character to the result, you can add the character to this string.
So you can write result = result + s[j];
Code snap should be like this -
string result = "";
//char result[len];
if (j < 0)
return s;
while(j >= 0) {
if (isVowel(s[j])) {
result = result + s[j];
}
j--;
}
But adding character to a string takes more run-time.
Besides this, you can also use string.push_back() to add a single character to a string. It's complexity is overall O(n), n = length of the final string.
string result = "";
//char result[len];
if (j < 0)
return s;
while(j >= 0) {
if (isVowel(s[j])) {
result.push_back(s[j]);
}
j--;
}

Related

What is wrong with my sentence palindrome code (C++)?

The below code is of a sentence palindrome checker. I'm a newbie programmer so I'm having a hard time debugging this code. It's showing incorrect output.
For example, if the sentence is "A man, a plan, a canal: Panama"
It's giving false as a result instead of true. (Blank spaces and punctuations are to be neglected.)
#include <iostream>
class Solution
{
public:
bool isPalindrome(std::string s)
{
int l, i;
i = 0;
l = s.length();
while (i <= (l - 1))
{
if (s[i] == ' ' || ispunct(s[i]) == true)
i++;
else if (s[l - 1] == ' ' || ispunct(s[l - 1]) == true)
l--;
else if (tolower(s[i]) == tolower(s[l - 1]))
{
l--;
i++;
}
else
return false;
}
return true;
}
};
int main(void)
{
Solution s;
const std::string text = "Panama";
std::cout << s.isPalindrome(text);
return 0;
}
The problem is very likely that the character classification functions (like e.g. ispunct) does not return a bool result.
They return an int whose value can be any non-zero value for "true".
That means the condition like ispunct(s[i]) == true might actually be false for punctuation characters.
You need to use e.g. ispunct(s[i]) != 0 (or just plain ispunct(s[i])) instead.
And also, that is not how one checks for palindrome. I suggest the following code:
#include <iostream>
int main() {
std::string str;
std::cin >> str;
for (auto i = 0, j = str.size() - 1; i < j; i++, j--) {
if (//check for punctuation) {
}
else if (str.at(i) != str.at(j)) {
//not a palindrome
break;
}
}
return 0;
}

Debug Assertion Failed error while comparing unicode using isdigit

I have the debug assertion error in the if statement when i = 7:
Expression: c>= -1 && c <= 255
This is my code:
#include <iostream>
#include <string>
const char* clearString(std::string str)
{
for (int i = str.length() - 1; i >= 0; i--)
{
if ( !isdigit(str[i])
&& str[i] != ',')
{
str.erase(i, 1);
}
}
return str.c_str();
}
int main()
{
std::string str = "688,13 €";
std::cout << clearString(str);
}
I try to delete all characters in the string that are not numbers and ','.
For std::isdigit, see the Notes section as to why you are getting the assertion.
The fix is to cast to an unsigned char:
if (!isdigit(static_cast<unsigned char>(str[i]))
Second, your function returns the address of a local temporary, thus exhibits undefined behavior. Return a std::string instead.
std::string clearString(std::string str)
{
//…
return str;
}
Third, you could rewrite your function using std::remove_if and std::string::erase, instead of writing a loop that removes a character at a time.
#include <algorithm>
//...
std::string clearString(std::string str)
{
auto iter = std::remove_if(str.begin(), str.end(),
[&](char ch)
{ return !isdigit(static_cast<unsigned char>(ch)) && ch != ',';});
str.erase(iter, str.end());
return str;
}
The function isdigit() works only with chars that their decimal value is between -1 and 255.
The decimal value of the character € is -128 which the function doesn't support.
I would suggest to change the comparison instead of using isdigit(), compare the decimal values of the chars.
Change your function to this:
const char* clearString(std::string& str)
{
for (int i = str.length() - 1; i >= 0; i--)
{
if ((str[i] < '0' || str[i] > '9') && str[i] != ',')
{
str.erase(i, 1);
}
}
return str.c_str();
}
A little out of topic, about your algorithm.
It would be better if you don't erase every non-digit character, but shift your characters left, skipping all non-digits (except ',') and resize string.
About isdigit I would do how 0xBlackMirror suggested, compare to '0' and '9'.
Here is the code:
const char* clearString(std::string str)
{
int j = 0;
for (uint i = 0; i < str.size(); i++)
{
if ((str[i] >= '0' && str[i] <= '9') || str[i] == ',')
{
str[j++] = str[i];
}
}
str.resize(j);
return str.c_str();
}

Leetcode 28 - Implement strStr(): question

I am experiencing a bug in my submissions for Leetcode 28 that has thus far eluded me. My code works for most test cases but I am getting hung up on scenarios such as haystack = "mississippi", needle = "issip".
I have tried debugging and found that the entire haystack string is iterated through and it is returning -1 or not found. The substring length it is finding at each occurrence of 'i' is 4, 1, 1.
int strStr(string haystack, string needle) {
if (needle.empty()) {
return 0;
}
if (haystack.empty() && !needle.empty()) {
return -1;
}
int i = 0, j = 0, ans = 0;
for (i; i < haystack.length(); i++) {
if (haystack[i] == needle[0]) {
j = 0;
ans = i;
for (j; j < needle.length(); j++) {
/*
if (haystack[i++] == needle[j]) {
continue;
}
else {
break;
}
*/
if (haystack[i++] != needle[j]) {
break;
}
}
if (j == needle.length()) {
return ans;
}
}
if (j == needle.length()) {
return ans;
}
}
return -1;
}
Input: "mississippi", "issip"
Output: -1 (ans = 10, j = 1)
The function has several drawbacks.
For starters it should be declared like
std::string::size_type strStr( const std::string &haystack, const std::string &needle );
and if the second string is not found in the first string the function should return std::string::npos as all similar member functions of the class std::string do.
The function parameters shell be of constant referenced types.
The condition in this if-statement
if (haystack.empty() && !needle.empty())
has a redundant operand. It could be rewritten like
if (haystack.empty())
This loop
for (i; i < haystack.length(); i++)
should stop its iterations when the size of the tail of the first string is less than the size of the second string.
in this if-statement
if (haystack[i++] != needle[j]) {
the variable i is incremented that results in incrementing the variable two times: one in this statement and the second time in the loop.
The second pair of these statements
if (j == needle.length()) {
return ans;
is redundant.
The function can be written the following way as it is shown in the demonstrative program.
#include <iostream>
#include <string>
std::string::size_type strStr( const std::string &haystack, const std::string &needle )
{
if ( needle.empty() )
{
return 0;
}
else if ( haystack.empty() )
{
return -std::string::npos;
}
else
{
std::string::size_type ans = std::string::npos;
auto n1 = haystack.length();
auto n2 = needle.length();
for ( std::string::size_type i = 0; ans == std::string::npos && i + n2 <= n1; i++ )
{
std::string::size_type j = 0;
while ( j < n2 && haystack[i+j] == needle[j] ) j++;
if ( j == n2 ) ans = i;
}
return ans;
}
}
int main()
{
std::string haystack( "mississippi" );
std::string needle( "issip" );
std::cout << strStr( haystack, needle ) << '\n';
return 0;
}
Its output is
4
The problem is that you modify i in
if (haystack[i++] != needle[j]) {
Thus preventing a second potential match from being explored. Try
if (haystack[i + j] != needle[j]) {
and fix any knock-on issues. I expect it to work as-is, though.

Sorting a vector of strings by the first letter in non-ascii order in C++

I have a text file with a list of words.
I used ifstream to read these words into a vector and now I am trying to sort them in an order similar to:
A a B b C c [...]
I tried to implement this using a third for loop inside of a bubble search algorithm to look at the first character of each word (I know this is far from the most efficient way especially if I was using a large data set)
And then check whether the letter and the next letter were uppercase or lowercase and switching if the uppercase letter was the same letter as the current letter, but this didn't seem to work.
void bubble_Sort (vector <string> & words)
{
for (unsigned i = words.size(); i >= 2; --i)
{
for (unsigned k = 0; k + 1 < i; k++)
{
int hi = k+1;
string temp1 = words[hi];
string temp2 = words[k];
int smallsize = words[hi].size();
int smallprecedence = 0;
if (words[k].size() < words[hi].size())
smallsize = words[k].size();
for (unsigned j = 0; j < smallsize; j++)
{
if (temp1[j] >= 'A' && temp1[j] <= 'Z')
{
if (temp2[j] >='a' && temp2[j] <= 'z')
{
char lowercase1 = temp1[j] + 32;
if (lowercase1 == temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
else if (temp2[j] >= 'A' && temp2[j] <= 'Z')
{
if (temp1[j] < temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
}
if (temp1[j] >= 'a' && temp1[j] <= 'z')
{
if (temp2[j] >= 'A' && temp2[j] <= 'Z')
{
char uppercase1 = temp1[j] - 32;
if (uppercase1 < temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
else if (temp2[j] >= 'a' && temp2[j] <= 'z')
{
if (temp1[j] < temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
}
else if (temp1[j] == temp2[j] && temp1.size() < temp2.size())
++smallprecedence;
}
if (smallprecedence == smallsize)
{
string temporary = words[k];
words[k] = words[hi];
words[hi] = temporary;
}
}
}
}
Don't reinvent the wheel. Just modify the default comparison function so aA < bB (regardless of case) and A < a.
EDIT I used the wrong comparison function. It should return true for <, and false for >=. This has been fixed
std::vector<std::string> vec;
//
std::sort(vec.begin(), vec.end(), [](const std::string& lhs, const std::string& rhs)
{
const char* s1=lhs.c_str();
const char* s2=rhs.c_str();
while(true) {
// first ignore case
if ( std::toupper(*s1) < std::toupper(*s2) ) return true;
if ( std::toupper(*s1) > std::toupper(*s2) ) return false;
// end of both strings, exact match
if ( *s1 == 0 && *s2 == 0 ) return false;
// compare upper case vs lower case ('A' vs 'a')
if ( *s1 > *s2) return false;
if ( *s1 < *s2) return true;
++s1; ++s2;
}
});
First, get rid of the hard-coded ASCII-isms. C and C++ have long had functions for determining whether a character is a letter, a digit, uppercase, lowercase, etc. Look them up.
Second, describe clearly what goes into determining the order that you want the result to be in.
Third, from that description, write a function that takes two strings, and tells you whether the first string should come before the second. Use that function in the sort.
You can sort a vector using std::sort and get a reference to the first character in a std::string using std::string::at() :
std::vector<std::string> vec;
//
std::sort(vec.begin(), vec.end(), [](const std::string& lhs, const std::string& rhs)
{
char l_ch, r_ch;
l_ch = lhs.at(0);
r_ch = rhs.at(0);
return l_ch < r_ch;
});
I think it's really enough to skip exactly equal prefixes and then compare once with uppercasing:
std::vector<std::string> vec;
//
std::sort(vec.begin(), vec.end(), [](const std::string& lhs, const std::string& rhs)
{
const char* s1=lhs.c_str();
const char* s2=rhs.c_str();
while(*s1 && *s1 == *s2) {++s1; ++s2;}
int rc = toupper(*s1) - toupper(*s2);
if (rc) return rc;
return *s1 - *s2;
});
If you need to compare by first letter only, simply remove while(*s1 && *s1 == *s2) {++s1; ++s2;}

About the solution for "reverse-string" in leetcode

https://leetcode.com/problems/reverse-string/
Here is my solution:
class Solution {
public:
string reverseString(string s) {
// if(s.size() == 0 || s.size() == 1)
// return s;
string::size_type i = 0;
string::size_type j = s.size() - 1;
while (i < j)
{
char temp = s[i];
s[i] = s[j];
s[j] = temp;
i++;
j--;
}
return s;
}
};
But this solution is not passed.
When i uncomment these two lines: if(s.size() == 0 || s.size() == 1) return s;, the code pass. i am confused, i think these code are equal.
When the length is 0 you get out of bounds access (j becomes a large number as it is unsigned).
The length 1 case should be safe to keep commented.