The goal of the program is to take a string like "kcck" and delete the consecutive duplicates. It should first iterate through the string and delete cc leaving kk; then go through again and delete kk; then return "empty" since there are no characters left in the string.
Another example, "aabggtcc" should return "bt".
int i;
int j = i+1;
string deduplicate(string input) {
for(i=0; i<input.length(); ++i) {
while(j <input.length()) {
if(input[i] == input[j]) {
input.erase(i);
input.erase(j);
}
else if (input[i] != input[j]) {
++i; ++j;
}
if(input[i] == '\0') {
cout<<"empty";
}
}
}
return 0;
}
int main () {
cout<<deduplicate("aabg")<<endl;
cout<<deduplicate("ag")<<endl;
cout<<deduplicate("btaabb")<<endl;
return 0;
}
When I run the code it gives me:
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
There are couple of issues with your snippet,
deduplicate function is returning zero(0) all the time
j is initialized in global scope and is never reset for new string
As you erase std::string::length() is calculated on new string hence your index i and j are won't point to same laocation.
Here is the snippet with rectified error,
string deduplicate(string input) {
int i = 0;
int j = 0;
while (i < input.length()) {
j = i + 1;
bool isRepeated = false;
while (j < input.length()) {
if (input[i] == input[j]) {
input.erase(j,1);
--j; //as string length is reduced by 1
isRepeated = true;
}
++j;
}
if (isRepeated) {
input.erase(i,1); //remove first letter as well
--i;//sting length is reduced by one
}
++i;
}
return input;
}
int main() {
std::cout << deduplicate("aabg") << endl;
std::cout << deduplicate("ag") << endl;
std::cout << deduplicate("btaabb") << endl;
return 0;
}
output:
bg
ag
t
which can be even simplified as,
std::string deduplicate(std::string input) {
std::string s ="";
for (auto c : input) //loop through all char
{
int f = 0;
for (auto c1 : input)
{
if (c1 == c)
{
f++; //increment if char is found
}
}
if (f == 1)//append char only if it present ones
s += c;
}
return s;
}
You are decreasing the size of the string whenever you call string.erase() that's why the variable i eventually exceeds the "current" string size input.length() in the while loop, and you get an error std::out_of_range: basic_string when you try to access input[i] in the if and else if conditions of the loop.
Try to manually go through the loop on the string on which you got the error and you will see that i has gone out of bound (i.e. i >= input.length()) in the while loop
With C++11 and on, instead of iterating over each character and making the comparison manually, you can use std::basic_string::find_first_not_of to look forward from a position in the string and find the first character not of the current character. If the position returned by .find_first_not_of is more than 1 from the current position, you can use .erase to erase that number characters. If the return is 1, then just increment your current position and repeat.
To operate on all duplicates characters in the modified string, you simply wrap it all in an outer-loop, keep a copy of the string before before entering the inner-loop to remove duplicate characters, and compare if the modified string is equal to your copy or the .length() is zero for your exit condition.
You can do something similar to the following:
#include <iostream>
#include <string>
int main (void) {
std::string s;
while (getline (std::cin, s)) {
std::string current;
do {
size_t pos = 0;
current = s;
while (pos < s.length()) {
size_t duplicates = s.find_first_not_of (s.at(pos), pos);
if (duplicates != std::string::npos && duplicates > pos + 1)
s.erase(s.begin() + pos, s.begin() + duplicates);
else if (duplicates == std::string::npos &&
(s.end() - s.begin() - pos) > 1)
s.erase(s.begin() + pos, s.end());
else
pos++;
}
} while (current != s && s.length());
std::cout << "'" << s << "'\n";
}
}
Example Use/Output
$ echo "kcck" | ./bin/ddcpp
''
$ echo "aabggtcc" | ./bin/ddcpp
'bt'
$ echo "aabg" | ./bin/ddcpp
'bg'
$ echo "ag" | ./bin/ddcpp
'ag'
$ echo "btaabb" | ./bin/ddcpp
'bt'
There are a number of ways to approach the problem and as long as they are reasonably efficient there isn't any one right/wrong way. If you have a modern C++ compiler, letting some of the built-in container functions handle the work is generally a bit more robust than reinventing it on your own. Look things over and let me know if you have questions.
Related
https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string/
In this Leetcode question, I tried to do it without using the concept of a stack. But according to the answer, I get the loop is not getting completed, why is that the case here?
class Solution {
public:
string removeDuplicates(string s) {
for (int i = 0; i<s.length(); i++) {
if(s[i] == s[i+1]){
s.erase(i,2);
i=0;
}
}
return s;
}
};
This is the error I am getting:
Your loop boundary, i < s.length(), is wrong since it'll let s[i + 1] access the string out of bounds*.
You need to reset i when a match is found, which you do, but it's followed by i++ directly, so it will never find a match at s[0] == s[1] again.
Fixed:
string removeDuplicates(string s) {
for (unsigned i = 0; i + 1 < s.length();) { // corrected loop bounds
if (s[i] == s[i + 1]) {
s.erase(i, 2);
i = 0;
} else ++i; // only add 1 if no match is found
}
return s;
}
* The out of bounds access will really access the terminating \0 (since C++11, undefined behavior before that), but it's unnecessary since you can't erase it anyway.
A somewhat quicker version would be to not reset i to 0, but to continue searching at the current position. You may also use std::adjacent_find to simplify the algorithm:
string removeDuplicates(string s) {
for(auto it = s.begin(); (it = std::adjacent_find(it, s.end())) != s.end();) {
it = s.erase(it, it + 2);
if(it != s.begin()) --it;
}
return s;
}
The main problem of this for loop
for (int i = 0; i<s.length(); i++) {
if(s[i] == s[i+1]){
s.erase(i,2);
i=0;
}
}
is that after erasing two adjacent elements the variable i is set to 0 within the if statement and then at once is incremented in the for statement. So in the next iteration of the loop the variable i is equal to 1.
Consider the following string
"abba'
after erasing "bb" the string becomes equal to "aa" but after that the variable i is equal to 1 and in the next iteration of the loop you are comparing s[1] and s[2] that are 'a' and '\0'.
Rewrite the loop at least the following way
for ( std::string::size_type i = 0; i < s.length(); )
{
if ( s[i] == s[i+1] )
{
s.erase(i,2);
i=0;
}
else
{
++i;
}
}
Pay attention to that according to the C++ Standard s[s.length()] is equal to '\0'. So you may use the comparison s[i] == s[i+1]. According to the assignment in the provided link the string contains only low case letters.
#include <iostream>
using namespace std;
int main() {
char a[101]{0};
cin>>a;
cin.getline(a,101);
cin.ignore();
int currLen{0};
int maxLen{0};
int startInd{-1};
int endInd{-1};
for(int i=0; i<101; i++) {
if(a[i]!=' ' ) {
++currLen;
} else if(a[i]==' '||a[i]=='\0') {
if(currLen>maxLen) {
maxLen=currLen;
startInd=i-currLen;
endInd=i-1;
}
if(a[i]=='\0')
break;
currLen=0;
}
}
cout<<maxLen<<endl;
if(startInd==-1)
cout<<-1;
else
for(int i=startInd; i<=endInd; i++)
cout<<a[i];
return 0;
}
If I take an input here, for example, "My name is Manav Kampani"
It will output 5
Manav instead of 7
Kampani
But if I write "My name is Manav Kampani ", with space after the last word
than it is considering Kampani too printing Kampani.
Also when I input "Kampani Manav is my name" then too it's displaying the wrong output. That means it is not considering the first word of the sentence.
if(a[i]!=' ' )
{
++currLen;
}
else if(a[i]==' '||a[i]=='\0')
{
....
}
Consider the case of a[i] == 0. Which of these if-statements will apply.
Answer: the first one. Which means you'll never look at the final word in the string. You also don't exit at the end of the string, but instead loop through whatever is in your string all the way out to character 101.
As a general structure, be very, very careful with this:
if (condition)
else if (condition)
// without a final else section
If you do that, you need to think about what you're doing. In this particular case, you can have:
if (a[i] != 0 && a[i] != ' ')
else
It may not solve all your issues, but it should solve some.
A nice sliding window pattern implementation.
You have 3 problems in your code
You must not write cin >> a;
You must not write cin.ignore();
You need to modify your if statement like so: if (a[i] != ' ' && a[i] != '\0') Otherwise you will not detect the last word.
Your complete working code with that minor fixes will lokk like that.
int main()
{
char a[101]{ 0 };
//cin >> a;
cin.getline(a, 101);
//cin.ignore();
int currLen{ 0 };
int maxLen{ 0 };
int startInd{ -1 };
int endInd{ -1 };
for (int i = 0; i < 101; i++)
{
if (a[i] != ' ' && a[i] != '\0')// Add comparison
{
++currLen;
}
else if (a[i] == ' ' || a[i] == '\0')
{
if (currLen > maxLen)
{
maxLen = currLen;
startInd = i - currLen;
endInd = i - 1;
}
if (a[i] == '\0')
break;
currLen = 0;
}
}
cout << maxLen << endl;
if (startInd == -1)
cout << -1;
else
for (int i = startInd; i <= endInd; i++)
cout << a[i];
return 0;
}
Additionally. You should not use C-Style arrays in C++. And please use std::string
There is a couple of things here:
1- You don't need to do a cin>>a this is actually consuming the first word, and afterwards the content is overrided by cin.getline(). So removing the firsst cin>>ayou'll be fine.
2- The last word is not read because there isn't any if condition that matches the condition aka.
if(a[i]!=' ' ) case of not a space
//not end of word
else if(a[i]==' '||a[i]=='\0') case of space or null
//end of word
So your last character is not a space nor null, that means you don't detect the last word.
I'm currently doing a leetcode question where I have to find a prefix within a sentence and return the word number within the sentence else return -1. I came up with a solution but it crashes with some strings and i dont know why. An example of this is the following:
Input: sentence = "i love eating burger", searchWord = "burg"
Output: 4 (I also get an output of 4)
Explanation: "burg" is prefix of "burger" which is the 4th word in the sentence.
but fails this example:
Input: sentence = "this problem is an easy problem", searchWord = "pro"
Output: 2 ( I get an output of 6)
Explanation: "pro" is prefix of "problem" which is the 2nd and the 6th word in the sentence, but we return 2 as it's the minimal index.
My cout for this one produced a very weird snippet:
problem is an easy problem
problem is an easy problem
problem is an easy problem
problem is an easy problem
probl
proble
problem
problem
problem i
problem is
it completely ignored the first couple substrings when i increments, this is the only time it happens tho.
int isPrefixOfWord(string sentence, string searchWord)
{
string sub;
int count = 1;
for (int i = 0; i < sentence.length(); i++)
{
if (sentence[i] == ' ')
count++;
for (int j = i; j < sentence.length(); j++)
{
sub = sentence.substr(i, j);
cout<<sub<<endl;
if (sub == searchWord)
{
return count;
}
}
}
return -1;
}
Any Ideas?
int isPrefixOfWord(string sentence, string searchWord)
{
string sub;
int count = 1;
for (int i = 0; i < sentence.length() - searchWord.length() - 1; i++)
{
if (sentence[i] == ' ')
count++;
sub = sentence.substr(i,searchWord.length());
if ( sub == searchWord && (sentence[i-1] == ' ' || i == 0))
{
return count;
}
}
return -1;
}
A very simple C++20 solution using starts_with:
#include <string>
#include <sstream>
#include <iostream>
int isPrefixOfWord(std::string sentence, std::string searchWord)
{
int count = 1;
std::istringstream strm(sentence);
std::string word;
while (strm >> word)
{
if ( word.starts_with(searchWord) )
return count;
++count;
}
return -1;
}
int main()
{
std::cout << isPrefixOfWord("i love eating burger", "burg") << "\n";
std::cout << isPrefixOfWord("this problem is an easy problem", "pro") << "\n";
std::cout << isPrefixOfWord("this problem is an easy problem", "lo");
}
Output:
4
2
-1
Currently, LeetCode and many other of the online coding sites do not support C++20, thus this code will not compile successfully on those online platforms.
Therefore, here is a live example using a C++20 compiler
We can just use std::basic_stringstream for solving this problem. This'll pass through:
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <string>
#include <sstream>
static const struct Solution {
static const int isPrefixOfWord(
const std::string sentence,
const std::string_view search_word
) {
std::basic_stringstream stream_sentence(sentence);
std::size_t index = 1;
std::string word;
while (stream_sentence >> word) {
if (!word.find(search_word)) {
return index;
}
++index;
}
return -1;
}
};
The bug that effects the function output is that you aren't handling the increment of i within your inner for loop:
for (int i = 0; i < sentence.length(); i++)
{
if (sentence[i] == ' ')
count++;
for (int j = i; j < sentence.length(); j++)
{
sub = sentence.substr(i, j);
cout<<sub<<endl;
if (sub == searchWord)
{
return count;
}
}
}
Notice that once your inner-loop is complete that i always iterates by one. So your next search through a word will incorrectly start at its next character, which incorrectly searches for "sub-words" instead of only prefixes, and so creates false positives (and unnecessary work).
Also note that every time that you do:
(sub == searchWord)
That this checks all j characters, even though we're only interested in whether the new jth character is a match.
Another bug, which effects your performance and your couts is that you're not handling mismatches:
if (sub == searchWord)
...is never false, so the only way to exit the inner loop is to keep increments j till the end of the array, so sub ends up being large.
A way to fix your second bug is to replace your inner loop like so:
if (sentence.substr(i, i + searchWord.length()) == searchWord)
return count;
and finally, to fix all bugs:
int isPrefixOfWord (const string & sentence, const string & searchWord)
{
if (sentence.length() < searchWord.length())
return -1;
const size_t i_max = sentence.length() - searchWord.length();
for (size_t i = 0, count = 1; ; ++count)
{
// flush spaces:
while (sentence[i] == ' ')
{
if (i >= i_max)
return -1;
++i;
}
if (sentence.substr(i, searchWord.length()) == searchWord)
return count;
// flush word:
while (sentence[i] != ' ')
{
if (i >= i_max)
return -1;
++i;
}
}
return -1;
}
Note that substr provides a copy of the object (it's not just a wrapper around a string), so this takes linear time with respect to searchWord.length(), which is particularly bad the word within sentence is smaller.
We can improve the speed by replacing
if (sentence.substr(i, searchWord.length()) == searchWord)
return count;
...with
for (size_t j = 0; sentence[i] == searchWord[j]; )
{
++j;
if (j == searchWord.size())
return count;
++i;
}
Others have shown nice applications of the libraries that help solve the problem.
If you don't have access to those libraries for your assignment, or if you just want to learn how you could modularise a problem like this without loosing efficiency, then here's a way to do it in c++11 without any libraries (except string):
bool IsSpace (char c)
{
return c == ' ';
}
bool NotSpace (char c)
{
return c != ' ';
}
class PrefixFind
{
using CharChecker = bool (*)(char);
template <CharChecker Condition>
void FlushWhile ()
{
while ((m_index < sentence.size())
&& Condition(sentence[m_index]))
++m_index;
}
void FlushWhiteSpaces ()
{
FlushWhile<IsSpace>();
}
void FlushToNextWord ()
{
FlushWhile<NotSpace>();
FlushWhile<IsSpace>();
}
bool PrefixMatch ()
{
// SearchOngoing() must equal `true`
size_t j = 0;
while (sentence[m_index] == search_prefix[j])
{
++j;
if (j == search_prefix.size())
return true;
++m_index;
}
return false;
}
bool SearchOngoing () const
{
return m_index + search_prefix.size() <= sentence.size();
}
const std::string & sentence;
const std::string & search_prefix;
size_t m_index;
public:
PrefixFind (const std::string & s, const std::string & sw)
: sentence(s),
search_prefix(sw)
{}
int FirstMatchingWord ()
{
const int NO_MATCHES = -1;
if (!search_prefix.length())
return NO_MATCHES;
m_index = 0;
FlushWhiteSpaces();
for (int n = 1; SearchOngoing(); ++n)
{
if (PrefixMatch())
return n;
FlushToNextWord();
}
return NO_MATCHES;
}
};
In terms of speed: If we consider the length of sentence to be m, and the length of searchWord to be n, then original (buggy) code had O(n*m^2) time complexity. But with this improvement we get O(m).
The problem is that it always outputs 0 (false) as a result. Probably the problem is in the isPalindrome function, but I cannot figure where exactly. Would be grateful if someone helped.
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
bool isPalindrome(string word)
{
bool result;
for (int i = 0; i <= word.length() - 1; i++)
{
if (word.at(i) == word.length() - 1)
{
result = true;
}
else
{
result = false;
}
return result;
}
}
int main()
{
string word1;
int count;
cout << "How many words do you want to check whether they are palindromes: " << flush;
cin >> count;
for (int i = 0; i < count; i++)
{
cout << "Please enter a word: " << flush;
cin >> word1;
cout << "The word you entered: " << isPalindrome(word1);
}
}
Try this one:
bool isPalindrome(string word)
{
bool result = true;
for (int i = 0; i < word.length() / 2; i++) //it is enough to iterate only the half of the word (since we take both from the front and from the back each time)
{
if (word[i] != word[word.length() - 1 - i]) //we compare left-most with right-most character (each time shifting index by 1 towards the center)
{
result = false;
break;
}
}
return result;
}
In this statement
if (word.at(i) == word.length() - 1)
the right side expression of the comparison operator is never changed and have the type std::string::size_type instead of the type char. You mean
if (word.at(i) == word.at( word.length() - 1 - i ))
However there is no sense to use the member function at. You could us the subscript operator. For example
if ( word[i] == word[word.length() - 1 - i ] )
And the loop should have word.length() / 2 iterations.
Also within the loop you are overwriting the variable result. So you are always returning the last value of the variable. It can be equal to true though a string is not a palindrome.
Also the parameter should be a referenced type. Otherwise a redundant copy of the passed argument is created.
The function can be defined the following way
bool isPalindrome( const std::string &word )
{
std::string::size_type i = 0;
std::string::size_type n = word.length();
while ( i < n / 2 && word[i] == word[n - i - 1] ) i++;
return i == n / 2;
}
Another approach is the following
bool isPalindrome( const std::string &word )
{
return word == std::string( word.rbegin(), word.rend() );
}
Though this approach requires to create a reverse copy of the original string.
The simplest way is to use the standard algorithm std::equal. Here is a demonstrative program
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
bool isPalindrome( const std::string &word )
{
return std::equal( std::begin( word ),
std::next( std::begin( word ), word.size() / 2 ),
std::rbegin( word ) );
}
int main()
{
std::cout << isPalindrome( "123454321" ) << '\n';
return 0;
}
I hope this one helps you also (corrected also warnings):
bool isPalindrome(string word)
{
bool result = false;
int lengthWord = (int)word.length();
for (int i = 0; i <= (lengthWord / 2); ++i)
{
if (word.at(i) == word.at(lengthWord - i -1))
{
result = true;
continue;
}
result = false;
}
return result;
}
Two possible problems.
You appear to be comparing a character to a number
if (word.at(i) == word.length() - 1)
shouldn't this be
if (word.at(i) == word.at(word.length() - i)) ?
There are 3 returns within the if statement, so no matter what the outcome it's only going to compare one character before returning to the calling function.
As a point of technique, repeated calls to .length inside the loop, which always returns the same value, wastes time and makes the code more difficult to understand.
You need to return as soon as you find a mismatch. If you are looking for a palindrome you only need to compare the first half of the word with the second half in reverse order. Something like
bool isPalindrome(string word)
{
for (int i = 0, j= word.length() - 1; i<j; i++, j--)
// i starts at the beginning of the string, j at the end.
// Once the i >= j you have reached the middle and are done.
// They step in opposite directions
{
if (word[i] != word[j])
{
return false;
}
}
return true;
}
The loop in the function isPalindrome will only execute once, because the return statement is unconditionally executed in the first iteration of the loop. I am sure that this is not intended.
To determine whether a string is a palindrome, the loop must be executed several times. Only after the last character has been evaluated (in the last iteration of the loop) will it be time to use the return statement, unless you determine beforehand that the string is not a palindrome.
Also, in the function isPalindrome, the following expression is nonsense, as you are comparing the ASCII Code of a letter with the length of the string:
word.at(i) == word.length() - 1
Therefore, I suggest the following code for the function:
bool isPalindrome(string word)
{
for (int i = 0; i < word.length() / 2; i++)
{
if (word.at(i) != word.at( word.length() - i - 1) ) return false;
}
return true;
}
As discussed in the comments under your question. You made some mistakes in the code.
Your function should more or less look like this:
bool isPalindrome(string word) {
bool result = true;
for (int i = 0; i <= word.length() - 1; i++)
{
if (word.at(i) != word.at(word.length() - 1 -i))
{
return false;
}
}
return result;
}
I have been implementing a factory for a component based game engine recently. I am deserializing objects by reading in from a file what component they need and what to initialize them with. It works except for when I try to read in a property longer than 15 characters. At 15 characters, it reads it in perfectly, anything longer and I get "ε■ε■ε■ε■ε■ε■ε■ε■ε" as output.
I am using std::string to store these lines of text.
Example:
JunkComponent2 test "1234567890123456" test2 "123456789012345"
With this the value of test becomes garbage, while test2 stays perfectly intact.
Any idea's what might be going on?
char line[1024];
while (file.getline(line, 1024))
{
std::vector<std::string> words;
std::string word;
int j = 0;
for (unsigned i = 0; line[i] != '\0' && i < 1024; ++i)
{
if (line[i] == ' ' && j > 0 && line[i - 1] != '\\')
{
words.push_back(word);
j = 0;
word = "";
}
else
{
++j;
word += line[i];
}
}
words.push_back(word);
// std::cout << (*Parts)["JunkComponent"]->GetName() << std::endl;
Component* c = (*Parts)[words[0]]->clone(words);
object->AddComponent(words[0], c);
for (std::list<Member*>::iterator it = members.begin(); it != members.end(); ++it)
{
for (unsigned i = 0; i < words.size(); ++i)
{
if ((*it)->GetName() == words[i])
{
if (words[i + 1][0] == '\"')
{
std::vector<char> chars;
chars.push_back('\"');
chars.push_back('\\');
for (unsigned int n = 0; n < chars.size(); ++n)\
{
words[i + 1].erase(std::remove(words[i + 1].begin(), words[i + 1].end(), chars[n]), words[i + 1].end());
}
Container((*it)->GetMeta(), GET_MEMBER(data.GetData(), (*it)->GetOffset()), (*it)->GetName()).SetValue<std::string>(words[i + 1]);
}
else
{
Container((*it)->GetMeta(), GET_MEMBER(data.GetData(), (*it)->GetOffset()), (*it)->GetName()).SetValue<int>(std::stoi(words[i + i]));
}
++i;
break;
}
}
}
}
GET_MEMBER Macro expands to:
#define GET_MEMBER(P, OFFSET) ((void *)(((char *)(P)) + (OFFSET)))
SetValue Function: (data is a void*)
template <typename T>
void SetValue(T data_)
{
memcpy(data, &data_, sizeof(T));
}
I'll take a stab having just eyed your code. GET_MEMBER is really nasty and I think that's where your problem is. It seems to rely on std::string being convertible to char*, which it is not. Why does your code work with strings shorter than 15? Well that's more than likely because std::string on most popular implementations actually contains a special case for strings where it keeps an internal buffer of length 16 ( last element \0 ) to avoid dynamic memory allocation. When the string is larger than 15 this buffer is uninitialized because it isn't used. The correct way to access the string is by using operator[].