I have created a class that is supposed to behave like std::string in a way. All of the functions that I have tested seem to work fine, but I am creating a justify method which is supposed to add spaces to a string in the already existing blank spots. The method is relatively simple:
String String::justify(const int Width) const
{
String result = *this;
int i = 0;
if(nextBlank(0) == -1 || length() > Width)
return *this;
while(result.length() < Width)
{
i = result.nextBlank(i);
if(i == -1) result.nextBlank(0);
result = result.substr(0,i) + ' ' + result.substr(i + 1, result.length());
i = result.nextNonBlank(i);
}
return result;
}
When this method is called I usually get a strange blend of chars because I think it's going past the Null terminating char somewhere, but I don't know where or why and that's what I'm hoping you guys could help me with. I believe it has to do with the substr method because that is when it outputs strangely. Even when not in the justify function if substr is called twice the output will be incorrect, but when called once it will work fine.
Also, here is the nextNonBlank, nextBlank, and substr functions because they are relevant:
int String::nextBlank(const int Index) const
{
int i = Index;
while(i <= length())
{
if(S[i] == ' ' && (i != Index || S[0] == ' '))
{
return i;
}
else if(i == length())
return -1;
++i;
}
return -1;
}
int String::nextNonBlank(const int Index) const
{
int i = Index;
while(i <= length())
{
if(S[i] != ' ' && i != Index )
{
return i;
}
++i;
}
return -1;
}
String String::substr(int Start, int End) const
{
String result;
assert(End <= length());
for(int i = Start; i <= End; ++i)
result = result + S[i];
return result;
}
The entire code is posted here if you guys think it might be a problem with the constructors or something else: http://pastebin.com/mTakMPtq
If anybody could take the time to look at this I would appreciate it immensely. I have been stuck on this for longer than I'd like to admit.
Here is an example test:
int main()
{
String str("Here is a string.");
std::cout << "Without justify:" << std::endl;
str = str.substr(0, 3) + " " + str.substr(3 + 1, str.length());
std::cout << str << "\n\n";
std::cout << "With justify:" << std::endl;
std::cout << str.justify(25);
}
And here is the output:
Without justify: ÐC*Here is a string.
With justify: ÐC*ÐC*Here ÐC* is a string.
Something is wrong with your substr() function. Added some variables for debugging and ran with 2 different strings. Here is the first iteration for each:
while(result.length() < Width)
{
i = result.nextBlank(i);
String temp1 = result.substr(0,i);
String temp2 = " ";
String temp3 = result.substr(i + 1, result.length());
int tmpint = result.length();
result = result.substr(0,i) + " " +result.substr(i + 1, result.length());
i = result.nextNonBlank(i);
}
i: 1
tmpint: 34
result: 0x603450 "A string without a beginning space"
tmp1: 0x6038b0 " with beginning space\021\001A "
tmp2: 0x603670 " "
tmp3: 0x603ad0 "x¦[÷ÿ\177string without a beginning space"
i: 0
tmpint: 24
result: 0x603450 " with beginning space"
tmp1: 0x6038b0 "x¦[÷ÿ\177x¦[÷ÿ\177x¦[÷ÿ\177x¦[÷ÿ\177\021\001 "
tmp2: 0x603690 " "
tmp3: 0x603ad0 " with beginning space"
Related
my attempt was trying to use find form aba / aa to compare each time, but it has some weird edge case , for example, the case "aaaa" was working fine, but "aaaaa" return only "aaaa" was incorrect.
I knew watching the discussion to find the answer for this, but I really want to know where I got wrong with my algorithm and see how can I improve this.
string longestPalindrome(string s) {
if(s.size() == 1)return s;
if(s.size() == 2 &&s[0]!= s[1]){
string x ="";
x+=s[0];
return x;
}
if(s.size() == 2 &&s[0]== s[1])return s;
if(s.size() == 3 && s[0]== s[2])return s;
string ans="";
ans+=s[0];//avoid to_string
for(int i =0; i < s.size();i++){ //c i=1 bb
string temp="";
if(s[i] == s[i+1]){//aa
temp+=s[i];
temp+=s[i+1];
int prev = i-1;
int curr = i+2;// scaacw caac
while(prev-1 != 0 && curr != s.size()){
if(prev== curr){
string x="";
x+=s[prev];
temp.insert(0,x);
temp+=s[curr];
x = "";
}else
break;
prev--;
curr++;
}
if(s[0]==s[s.size()-1]){
string x="";
x+=s[0];
temp.insert(0,x);
temp+=s[s.size()-1];
}
if(temp.size()>ans.size()){
ans = temp;
temp = "";
}
}
else if(s[i] == s[i+2]){//i =3
temp+=s[i];
temp+=s[i+1];
temp+=s[i+2];
int prev = i-1;
int curr = i+3;
while(prev-1 != 0 && curr != s.size()){
if(prev== curr){
string x="";
x+=s[prev];
temp.insert(0,x);
temp+=s[curr];
x = "";
}else
break;
prev--;
curr++;
}
if(s[0]==s[s.size()-1]){
string x="";
x+=s[0];
temp.insert(0,x);
temp+=s[s.size()-1];
}
if(temp.size()>ans.size()){
ans = temp;
temp = "";
}
}
}
return ans;
}
};
So the first problem that you also wrote was that you get "aaaa" instead of "aaaaa". That is because in your for loop, in the first if statement, you check for "aa", so 2 same characters next to eacht other. Then you try something, I guess to go "left" and "right" in the string. But this "if(prev== curr)" does not make much sense, I think you wanted to write "s[prev]" and "s[curr]", because I am not sure how they could ever be equal. Nonetheless, another problem is that when you are looking for the "aa" in the string, you do not find the "middle" "aa", but the first "aa". Then you check if the first letter, which is "a" and the last letter, which is "a" as well are the same. They are, so you add it to your string, hence the "aaaa". If you run it for "1aabc1" it will give you "1aa1", because of the same reasons. You find "aa", then you check if the first equals the last, and you add it.
I think it is a good brain exercise to write these things on your own, but you are not on the correct track yet.
To maybe help a bit, your approach seems to be building the palindrome. So you are not taking substrings and checking, but trying to build it up. You see that correctly, that either you are looking for an "aba"-like or an "aa"-like palindrome. I tried to follow your logic, and came up with the following function:
std::string longestPalindrome2(const std::string& s) {
size_t max_palindrome_length = 0;
int max_palindrome_start = 0;
for (int i = 0; i < s.size(); ++i) {
int down = i - 1;
int up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
down = i;
up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
}
std::string substring = s.substr(max_palindrome_start, max_palindrome_length);
if (substring.empty()) substring = s.substr(0, 1);
std::cout << "substring '" << substring << "' IS a palindrome " << std::endl;
return substring;
}
I tried to do what you did, so building up the palindrome by checking the lower and upper neighbours. Although I am not a 100% sure if this is correct, it seems to work with my test cases.
I also wrote another function, which follows a "top-down" approach instead of your "bottom-up" one. With the test cases, the whole code looks like this:
bool is_it_palindrome(const std::string& str) {
/* loop through the string until the middle is reached */
for (int i = 0; i < str.size() / 2; ++i) {
/* if the mirrored pairs are not the same, it is not a palindrome */
if (str[i] != str[str.size() - 1 - i]) return false;
}
/* all mirrored pairs were the same */
return true;
}
std::string longestPalindrome(const std::string& s) {
/* the first substring will be the whole string itself */
int substring_size = s.size();
/* loop and reduce the substring size in each iteration */
/* so if we assume a string of size 5, we fist take all the possible */
/* 5 long substrings (only one such substring exists, the whole string) */
/* then we take the 4 long substrings, there are 2 of them, then the 3 */
/* long substrings, etc */
while (substring_size > 0) {
/* loop through the characters and get the substring from each character */
/* then check if this substring is a palindrome */
/* since we are looping from the largest substring possible to smaller ones */
/* it will definitely find the largest on first */
for (int i = 0; i < s.size(); ++i) {
if (i + substring_size <= s.size()) {
std::string substring = s.substr(i, substring_size);
if (is_it_palindrome(substring)) {
std::cout << "substring '" << substring << "' IS a palindrome " << std::endl;
return substring;
}
//std::cout << "substring '" << substring << "' is NOT a palindrome " << std::endl;
}
}
--substring_size;
}
return "";
}
std::string longestPalindrome2(const std::string& s) {
size_t max_palindrome_length = 0;
int max_palindrome_start = 0;
for (int i = 0; i < s.size(); ++i) {
/* search for the "aba" style palindrome */
int down = i - 1;
int up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
/* search for the "aa" style palindrome */
down = i;
up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
}
std::string substring = s.substr(max_palindrome_start, max_palindrome_length);
if (substring.empty()) substring = s.substr(0, 1);
std::cout << "substring '" << substring << "' IS a palindrome " << std::endl;
return substring;
}
int main() {
longestPalindrome("aaaaa");
longestPalindrome2("aaaaa");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("1aabc1");
longestPalindrome2("1aabc1");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("21");
longestPalindrome2("21");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("2asddsa1");
longestPalindrome2("2asddsa1");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("a");
longestPalindrome2("a");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("abcdefgh");
longestPalindrome2("abcdefgh");
return 0;
}
And the results are the following:
substring 'aaaaa' IS a palindrome
substring 'aaaaa' IS a palindrome
----------------------------------------------
substring 'aa' IS a palindrome
substring 'aa' IS a palindrome
----------------------------------------------
substring '2' IS a palindrome
substring '2' IS a palindrome
----------------------------------------------
substring 'asddsa' IS a palindrome
substring 'asddsa' IS a palindrome
----------------------------------------------
substring 'a' IS a palindrome
substring 'a' IS a palindrome
----------------------------------------------
substring 'a' IS a palindrome
substring 'a' IS a palindrome
I was working on a leetcode problem to reverse a string such that such that if Input is The boy is mad, the output should be mad is boy The. I solved it but the solution seemed poor as I was running through the string a lot of times. On submitting the code, the time taken to compute all the test cases was ~250ms, and I believed it as I knew this was the worst possible code. I looked up one of the answers and ran that code, it ran in like 4ms. I wrote the same code again, but this time I used a = a + b; instead of a += b;, and the test cases again took around 250ms. So, I want to know what is the difference between these two and why does it take a long time on one when compared to the other.
PS - In my original (poor) code, on changing a = a + b to a += b, the code ran in like 8ms.
Here are the codes:
1. slow code(runtime - ~250 ms)
string reverseWords(string s) {
if(s.length() == 0)
{
return s;
}
int itr = 0;
// Remove white spaces from front
while(s[itr] == ' ')
{
itr++;
}
s = s.substr(itr,s.length()-itr);
if(s.length() == 0)
{
return s;
}
// Remover white spaces from back
for(int i = s.length()-1; i>= 0 ;i--)
{
while(s[i] == ' ')
{
s = s.substr(0,i);
i--;
//cout << s << endl;
}
break;
}
if(s.length() == 0)
{
return s;
}
cout << s << endl;
// Remove more than one space in the middle
for(int i = 0;i<s.length()-1;i++)
{
if(s[i] == ' ' && s[i+1] == ' ')
{
string temp;
temp = s.substr(0,i);
s = s.substr(i+1,s.length());
s = temp + s;
i--;
//cout << s <<endl;
}
}
string res = "";
reverse(s.begin(),s.end());
int start = 0;
for(int i = 0;i <s.length();i++)
{
start = i;
if(s[i] == ' ')
continue;
while(s[i] != ' ' && i < s.length())
{
i++;
// cout << i << " ";
}
// cout << endl;
string temp = s.substr(start,i-start);
reverse(temp.begin(),temp.end());
if(i != s.length())
res = res + temp + " ";
else
res = res + temp;
//cout << temp << endl;
}
return res;
}
Fast code(4ms)
string reverseWords(string s)
{
stack<string> elements;
std::istringstream stream(s);
std::string word;
while (stream >> word)
{
elements.push(word);
}
string strReturn("");
int stackSize = elements.size();
int i = 0;
while( !elements.empty())
{
i++;
strReturn += elements.top();
elements.pop();
if( i != stackSize)
{
strReturn += " ";
}
}
return strReturn;
}
Re written Fast code(250ms)
string reverseWords(string s) {
// Check the first available Accepted solution to see the shitiest solution for the code
stack<string> availableWords;
istringstream stream(s);
string word;
while(stream >> word)
{
availableWords.push(word);
}
int len = availableWords.size();
string res = "";
int i = 0;
while(!availableWords.empty())
{
res += availableWords.top();
availableWords.pop();
i++;
if(i != len)
{
res += " ";
}
}
return res;
}
I have made this code such that whatever I type in a sentence has the first letter of the first word capitalized; While reducing any number of spaces in a sentence to just one space. However, my sentences are only reducing by one space. For example, if I put 3 spaces in a sentence, the output has spaces reduced by 1 to 2 spaces, but I want the output of words in a sentence to have only one space. I can't quite figure out what is wrong with my code and hence any help would be greatly appreciated. I have attached my code for reference below:
#include <stdio.h>
#include <cstring>
#include <iostream>
using namespace std;
int main()
{
int i = 0; //i for counter
string str;
//String variable
getline(cin, str); //Get string from user
int L = str.length(); //Find length of string
//Display original string
for (int i = 0; i < 100; i++)
{
str[i] = tolower(str[i]);
}
str[0] = toupper(str[0]);
bool space;
for (int j = i + 1; j < L; j++)
{
str[j] = str[j + 1];
L--;
}
cout << str << endl;
return 0;
}
Or doing it in a more modern way using iterators :
#include <iostream>
#include <cctype>
int main() {
std::cout << "This is the string trimming function.\n" <<
"Throw in a string and I will make sure all extra spaces " <<
"will be reduced to single space.\n";
std::string InputString, TrimmedString;
int head = -1;
int tail = -1;
std::cout << "Please enter the input string :\n" << std::endl;
std::getline(std::cin, InputString);
for(std::string::iterator it = InputString.begin(); it <= InputString.end(); it++){
if (*it != ' ' && head < 0 && tail < 0) {
head = std::distance(InputString.begin(), it);
}
else if (head >= 0 && tail < 0 && (*it == ' ' || it == InputString.end())) {
tail = std::distance(InputString.begin(), it);
TrimmedString.append(InputString, head, (tail-head));
TrimmedString.push_back(' ');
head = -1;
tail = -1;
}
}
TrimmedString[0] = toupper(TrimmedString[0]);
std::cout << "\nThe processed string is :\n\n" << TrimmedString << std::endl;
return 0;
}
Try this:
int main()
{
std::string str;
std::getline(cin, str);
//Change case
str[0] = toupper(str[0]);
std::transform(str.begin() + 1, str.end(), str.begin() + 1, ptr_fun<int, int>(tolower));
//Parsing time
for (int i = 0; i <= str.size() - 1; i++)
{
if (str[i] == ' ' && str[i + 1] == ' ') //if present & next are whitespaces, remove next
{
str.erase(str.begin() + i);
i--; // rechecking condition
}
}
std::cout << '\n' << str << '\n';
}
Output:
I'm going through a coding interview book and got stuck on a question: Replace all spaces in a string with '%20'.
I tried running this solution in my compiler but got this error: String Subscript Out Of Range. So, I looked up stackoverflow for that error and got a solution to try to append new chars with += instead of just assigning new chars to the string but that still produced the same error.
Here's my code. Thanks so much for your time!
void replaceSpaces(string &str)
{
int spaces = 0;
// Count number of spaces in original string
for (int i = 0; i < str.size(); i++)
{
if (str[i] == ' ')
spaces++;
}
// Calculate new string size
int newSize = str.size() + (2 * spaces);
str.resize(newSize); // thanks Vlad from Moscow
// Copy the chars backwards and insert '%20' where needed
for (int i = str.size() - 1; i >= 0; i--)
{
if (str[i] == ' ')
{
str[newSize - 1] = '0'; // += '0' didnt work
str[newSize - 2] = '2'; // += didnt work
str[newSize - 3] = '%'; // same
newSize = newSize - 3;
}
else
{
str[newSize - 1] = str[i]; // same
newSize--;
}
}
}
int main()
{
string test = "sophisticated ignorance, write my curses in cursive";
replaceSpaces(test);
cout << test << endl;
}
You did not resize string str.
You set variable newSize
int newSize = str.size() + (2 * spaces);
larger than str.size() and use it like an index in str
str[newSize - 1] = str[i];
At least you could write at first
str.resize( newSize );
Here is a demonstrative program that shows how the function can be written
#include <iostream>
#include <string>
std::string & replaceSpaces( std::string &s )
{
std::string::size_type spaces = 0;
// Count number of spaces in original string
for ( char c : s ) if ( c == ' ' ) ++spaces;
if ( spaces != 0 )
{
auto i = s.size();
// Calculate new string size
auto j = s.size() + 2 * spaces;
s.resize( j );
// Copy the chars backwards and insert '%20' where needed
while ( i != j )
{
if ( s[--i] == ' ' )
{
s[--j] = '0';
s[--j] = '2';
s[--j] = '%';
}
else
{
s[--j] = s[i];
}
}
}
return s;
}
int main()
{
std::string test = "sophisticated ignorance, write my curses in cursive";
std::cout << "\"" << test << "\"\n";
std::cout << "\"" << replaceSpaces( test ) << "\"\n";
}
The program output is
"sophisticated ignorance, write my curses in cursive"
"sophisticated%20ignorance,%20write%20my%20curses%20in%20cursive"
EDIT: After you inserted a statement with resize as I adviced then in the loop
for (int i = str.size() - 1; i >= 0; i--)
^^^^^^^^^^^^^^^^^^^^^^
variable i must be initialized with the old size of the string that it had before resizing it.
If you are looking for a practical solution without being overly concerned with performance, here's something much simpler:
void replaceSpaces(string &str) {
str = std::regex_replace(str, std::regex(" "), "%20");
}
How about this?
#include <iostream>
#include <string>
std::string replaceSpaces(std::string str)
{
std::string newStr;
for (char c : str)
{
if (c == ' ')
newStr.append("%20");
else
newStr.push_back(c);
}
return newStr;
}
int main()
{
std::string test = "sophisticated ignorance, write my curses in cursive";
std::string newtest = replaceSpaces(test);
std::cout << test << std::endl << newtest << std::endl;
}
I was trying to test my self and wanted to write mergesort, without actually looking up any code online, and to do it in a certain time period. I am stuck at this point where I cannot simply understand what I am doing wrong, since merge sort, as much as i remember, is to divide the strings up to the point where string is only 1 character and later on merge them back together. The code I've written below tries to do the exact thing. I was wondering whether I got the concept wrong, or just my implementation?
string merge(string str1, string str2) {
string final = "";
int i = 0, j = 0;
bool fromStr1 = false;
while(true) {
if(str1[i] < str2[j]) {
final += str1[i];
i++;
if(i == str1.size()) {
break;
}
}
else {
final += str2[j];
j++;
if(j == str2.size()) {
break;
fromStr1 = true;
}
}
}
if(fromStr1) {
for(int t = i; t < str1.size(); t++) {
final += str1[t];
}
}
else {
for(int t = j; t < str2.size(); t++) {
final += str2[t];
}
}
return final;
}
string mergeSort(string str1, int start, int end) {
if(end - start == 1)
return str1;
else {
int pivot = (end - start) / 2;
string newStr1 = mergeSort(str1, start, pivot);
string newStr2 = mergeSort(str1, pivot + 1, end);
return merge(newStr1, newStr2);
}
}
Note the changes:
#include <iostream>
#include <string>
using namespace std;
string merge(string str1, string str2) {
string final = "";
int i = 0, j = 0;
bool fromStr1 = false;
while (true) {
if (i >= (int)str1.size()) {
break;
}
if (j >= (int)str2.size()) {
fromStr1 = true; // changed the order of this with break!
break;
}
if (str1[i] < str2[j]) {
final += str1[i];
i++;
}
else {
final += str2[j];
j++;
}
}
if (fromStr1) {
for (int t = i; t < (int)str1.size(); t++) {
final += str1[t];
}
}
else {
for(int t = j; t < (int)str2.size(); t++) {
final += str2[t];
}
}
return final;
}
string mergeSort(string str1) {
int len = str1.size();
if (len <= 1)
return str1;
else {
string newStr1 = mergeSort(str1.substr(0, len / 2));
string newStr2 = mergeSort(str1.substr(len / 2, len - len / 2));
return merge(newStr1, newStr2);
}
}
int main()
{
cout << '"' << mergeSort("") << '"' << endl;
cout << '"' << mergeSort("a") << '"' << endl;
cout << '"' << mergeSort("ba") << '"' << endl;
cout << '"' << mergeSort("132") << '"' << endl;
cout << '"' << mergeSort("4321") << '"' << endl;
cout << '"' << mergeSort("54321") << '"' << endl;
return 0;
}
Output (ideone):
""
"a"
"ab"
"123"
"1234"
"12345"
This doesn't look right:
int pivot = (end - start) / 2;
string newStr1 = mergeSort(str1, start, pivot);
string newStr2 = mergeSort(str1, pivot + 1, end);
Don't you mean pivot=(end+start)/2? Or else mergeSort(str1, start, start+pivot) and mergeSort(str1, start+pivot+1, end)?
EDIT:
And your merge doesn't cope well with empty strings. You should have tested this function before hooking it up to mergeSort.
It's been ages since I used C++, but doesn't break immediately exit the loop? Because fromStr1 = true; is never reached in that case.