Remove Consecutive Duplicates Recursively giving infinite recursion - c++

Not a homework question.
I am self learning.
I have to remove consecutive characters in a string by recursion. However the program I made is not working for inputs containing duplicates. It is goining in infinite recursion and hence gives segmentation fault. However it is working for inputs which doesn't have consecutive duplicates in them. I have tried debugging in Eclipse Ide but things get weird when I debug. (I know how to debug) but I can't figure out the things are different when I debug and when I run. I will give you example after my code.
#include <iostream>
#include <cstring>
using namespace std;
void removeConsecutiveDuplicates(char *input) {
int l = strlen(input);
if(l == 0) {
return;
}
if(input[0] != input[1]) {
removeConsecutiveDuplicates(input+1);
return;
}
int i = 1;
for(; input[i] != '\0'; ++i) {
input[i-1] = input[i];
}
input[i] = '\0';
removeConsecutiveDuplicates(input);
}
int main(void) {
char ch[1000];
cin >> ch;
cout<<"The String Before Removing Duplicates : "<<ch<<endl;
removeConsecutiveDuplicates(ch);
cout<<"The String After Removing Duplicates : "<<ch<<endl;
return 0;
}
When I am debugging this code I and seeing the variable l value it is 16. This doesn't happen while running. What I am missing here?

This line :
input[i] = '\0';
doesn't do anything (input[i] already has that value, because that was the end condition for the loop). You then call the removeConsecutiveDuplicates function recursively, but the length of the string is still the same, so you'll keep making recursive calls until a stack overflow happens.
Instead, you need to make the string length smaller :
input[i - 1] = '\0';
to avoid this infinite recursion.

To pass your self-assigned homework problem :), explain why the following line never has any effect in your code:
input[i] = '\0';

In your function, change this:
input[i] = '\0';
to this:
input[i-1] = '\0';
since you have chopped away a character, thus you should decrease the size of your string.
Without that, you wouldn't reduce the size, thus looping over and over again the same size, without it changing, would result in an infinite loop (since the size wouldn't change).
I suggest you use an std::string next time, since this is C++.

Related

What's the difference between these two given examples

I'm a beginner in C++ and programming itself actually. I just want to ask, What's the difference between these 2 examples. What is the difference between "len = strlen(str1)-1" and "i = strlen(str1)-1"
Top part of the code will be like this:
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
char str1[20],str2[20];
int c, i ,j, len;
cout<<"Enter a word: ";
cin.getline(str1, 20);
Example 1:
//reverse
for (i = strlen(str1)-1, j = 0; i >= 0; i--, j++){
str2[j] = str1[i];
}
//compare string
c = strcmp(str1, str2);
/*This does not work because the value of 'c' will be -1 if the input
is "lol" which is palindrome*/
and Example 2:
//reverse
len = strlen(str1)-1;
for (i = len, j = 0; i >= 0; i--, j++){
str2[j] = str1[i];
}
//compare string
c = strcmp(str1, str2);
/*This does work in other hand, because of the variable "len"*/
the rest of the code will be like this
if(c == 0){
cout<<"It is a Palindrome";
}
//if the value of C is !=0
else{
cout<<"It is not a Palindrome";
}
}
Why is that? Thanks in advance for those who will answer. :)
Both examples are same except first uses an extra variable len.
This code is actually reversing the string. If str1 contains "123" then str2 will contain "321".
Function strlen(str1) returns the length of str1 but in C++ index of Arrays start from 0 that is why the last element index will be one less than length, hence strlen(str1) - 1.
UPDATE
Even with updated information the answer to first question remains same that both examples are same in nature. Difference in results is a mare co-incident due to a reason explained below.
char str1[20],str2[20];
This code creates two array of 20 char but not initialized. This means the initial values can be random.
Now when you call cin.getline(str1, 20); it not only writes the string you entered but adds a terminating '\0' character at the end of it. Our reversing logic only reverse the string but does not insert terminating '\0' at the end of str2 which means str2 is much longer (until it finds a '\0') than str1. Due to this they never compare correctly.
A simple solution to this issue can be zero-filling the arrays before using them and in C++ there is a simple way to do that:
char str1[20] = { 0 }, str2[20] = { 0 };
It is always a good practice to zero-fill your arrays if you are going to use then as strings.

Finding letters in a string while ignoring everything else

For my homework, part of what I need to do is take a phrase from the user, and from there take only the letters in the phrase, ignoring numbers, spaces, and special characters. Once I find letters in the string, I need to store them into a separate variable. However, I can't get that variable to store anything outside of the if statement that looks for letters.
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line, temp;
cout << "Enter phrase to check: ";
getline(cin, line);
for(int i = 0; i < line.size(); i++)
{
if((line[i] > 64 && line[i] < 91) || (line[i] > 96 && line[i] < 123))
{
temp[i] = line[i];
}
}
cout << temp;
return 0;
}
When I run the program, temp outputs nothing. But I know the if statement is correctly finding letters, from making it print line[i] inside the if statement.
your temp variable is an empty string. temp[x] is telling the compiler to change the x-th character of that string(which doesn't make any sense, as the string doesn't have any characters!). You're lucky(or unlucky) that you aren't getting any Segmentation faults(crashes).
Just use the += operator:
temp += line[i];
Try
temp.push_back(line[i]);
It will work.
The way you're currently doing it (temp[i] = line[i];) means that each non special character in line will be placed at the same index in temp. This should usually fail since temp (a string) does not resize on indexing.
For changing the size of a string, there exists a function called string::push_back as detailed here.
Instead of indexing using temp[i], you would instead use temp.push_back(line[i]);
This function allows the string to resize itself to accommodate the new char if need be and won't throw a segmentation fault.
NB: std::string::push_back is designed to append a single char to a string. There exist multiple other ways of doing this, including Nikita Demodov's answer which shows the use of the += operator which is much more lenient and will allow appending of other strings etc. push_back is most common to the std::vector where it is used to append single items to the list.

Failed to generate executive file in Visual Studio Code 2019

I am new in C++.
I encountered in question to find the vowels(i.e. "a","e","i","o","u") in a sub string,
and then I was thinking about change the driver function to get user input.
Unfortunately, this code did not generate execution file in Visual Studio Code nor any error messages.
Any help would be highly appreciated.
FYI, the function code was from GeeksforGeeks.
Thanks!
#include <stdio.h>
#include <string>
using namespace std;
// return true if x is vowel
bool isVowel(char x){
// function to check if x is in vowel or not
return (x == 'a' || x == 'e' || x =='i' || x == 'o' || x=='u');
}
void(FindSubString(std::string str)){
set<char> hash; // to store vowel
// outer loop picks starting characters
// and inner loop picks for ending characters
int n = str.length();
for (int i =0; i<n; i++){
for (int j = i; j <n; j++){
// if current position is not vowel,
// then no more possible string starting from str[i]
if (isVowel(str[j])==false)
break;
// if vowel, insert into hash
hash.insert(str[j]);
//if all vowels are present in current substring
if (hash.size()==5)
cout << str.substr(i, j-i+1) << " ";
}
hash.clear();
}
int main()
{
string str = getstring("insert a string: %s\n", stdin);
FindSubString(str);
return 0;
}
}
So, as mentioned in the comments, there are a number of fatal compiler errors in your code (when I run it through Visual Studio's C++ compiler as is, I get 7 errors - so I'm not sure how you are seeing none).
First, you have a missing closing brace (}) at the end of that function (seemingly, it is after the body of main, instead). Moving the } from the very end of the code to its proper place at the end of the FindSubString function will fix that. (This may be a copy-paste error, or you may be trying to define main inside FindSubString, but that's not allowed in C++.)
Second, you have the wrong and missing header files. Generally, for C++ programs, you should use the <iostream> header rather than <stdio.h>. The latter is generally used for C programs; however, it can be used in C++, but you'll need <iostream> if you want to use std::cout and std::cin. (I'm not sure if this is part of the Standard, but many implementations automatically include stdio.h when you include iostream.) You also need to #include <set> to make use of the std::set container.
And, the last of the fatal errors is your call to getstring. This is not a standard library function. So, you can either define this yourself, or just use the code in the main function below:
int main()
{
string str;
cout << "insert a string: ";
cin >> str;
// string str = getstring("insert a string: %s\n", stdin);
FindSubString(str);
return 0;
}
There is also a 'peculiarity' (though not actually an error, as my first version of this answer suggested) in the way you define your FindSubString function, with the 'extra' (unneeded, and very confusing) set of parentheses. It should be just this:
void FindSubString(std::string str)
{
However, even with all these errors fixed, your code does not work! This is because of a flaw in your logic, in the inner for loop of your FindSubString function. As you have it, that loop will terminate (because of the break; statement) on the first occurrence of a non-vowel.
You should, instead, check if the test letter is a vowel, and insert into the hash set if so. I'm not entirely sure what your definition of a sub-string is, but this code does something approaching what I think you want (feel free to clarify your goal, or correct my assumption):
void FindSubString(std::string str)
{
set<char> hash; // to store vowel
int n = str.length();
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if (isVowel(str[j])) hash.insert(str[j]); // if vowel, insert into hash
//if all vowels are present in current substring
if (hash.size() == 5)
cout << str.substr(i, j - i + 1) << " ";
}
hash.clear();
}
}
The code shown will find all substrings containing the five vowels.
Sample input:
facetiously
Output:
facetiou facetious facetiousl facetiously acetiou acetious acetiousl acetiously

Recursive finding and filling the multiplication of given array value with all previous array values

#include <iostream>
using namespace std;
//hope to get answer using recurssion
void atoA(int a[],int A[],int i,int size){
if(i<size){
if(i==0){ A[i]=a[i];}
else{
A[i] = a[i]+A[i-1];
}
atoA(a,A,i++,size);
}
}
int main(){
int a[]={1,8,4,7,6,2,3,9};
int size = sizeof(a)/sizeof(a[1]);
int A[size],i,j;
atoA(a,A,0,size);
/*The answer without recurssion
for(i=0;i<size;i++){
if(i==0) A[i]=a[i];
else A[i] = a[i]*A[i-1];
}
*/
for(j = 0;j < size ; j++){
printf(" %d ",A[j]);
}
}
So for array
{2,4,3,6}
Output should be
{2,8,24,144}
My iterative version is OK
I tried doing recurrsively modifying and try to print them but could not get the answer it say
Segmentation fault : CORE DUMP
Can someone point out where i did wrong and what should i modify.Somwhere in calling the function again i feel somewhere it keeps going. I did specify all the constraints but still there seems to be someproblem that persists and i cannot pin point the location.
There are two problems in your code. First, the i++ is not taking effect before you recurse, thus making your recursion go infinite (it's called with i equal to 0 every time). This is the cause of your segmentation fault (you're blowing the stack). Use ++i instead. The second problem is A[i] = a[i]+A[i-1];, which should obviously be A[i] = a[i]*A[i-1]; if you're trying to multiply numbers.
The recursive call
atoA(a,A,i++,size);
is not right. Due to the use of the post-increment operator, it is the same as:
atoA(a,A,i,size);
i = i + 1;
Because of that, you get infinite recursion and stack overflow. Change that line to:
atoA(a,A,i+1,size);
Also, you have a mismatch between the computations in the two versions.
In the recursive version, you have:
A[i] = a[i]+A[i-1];
In the iterative version, you have:
else A[i] = a[i]*A[i-1];
One of them needs to be changed to see matching results.
I have not work with C++ since my first semesters at the university. Thus you need to tweak and check the following code:
boolean mult (int accValue, int pos, int A[])
{
int value = accValue*A[pos];
if(pos == 0)
printnf("[ ");
else
printnf(",");
printf(" %d ", value)
if(pos = a.lenght()-1)
{
printnf("]");
return false;
}
else
{
return mult(value, pos++, A);
}
}
running code
mult(1, 0, A[]);
This is a recursive function which will iterate until the trivial case, in this case the condition if(pos = a.lenght()-1) which it will be the last iteration.

C++ Dynamic Array Inputs

I am using two dynamic arrays to read from a file. They are to keep track of each word and the amount of times it appears. If it has already appeared, I must keep track in one array and not add it into the other array since it already exists. However, I am getting blank spaces in my array when I meet a duplicate. I think its because my pointer continues to advance, but really it shouldn't. I do not know how to combat this. The only way I have was to use a continue; when I print out the results if the array content = ""; if (*(words + i) == "") continue;. This basically ignores those blanks in the array. But I think that is messy. I just want to figure out how to move the pointer back in this method. words and frequency are my dynamic arrays.
I would like guidance in what my problem is, rather than solutions.
I have now changed my outer loop to be a while loop, and only increment when I have found the word. Thank you WhozCraig and poljpocket.
Now this occurs.
Instead of incrementing your loop variable [i] every loop, you need to only increment it when a NEW word is found [i.e. not one already in the words array].
Also, you're wasting time in your inner loop by looping through your entire words array, since words will only exist up to index i.
int idx = 0;
while (file >> hold && idx < count) {
if (!valid_word(hold)) {
continue;
}
// You don't need to check past idx because you
// only have <idx> words so far.
for (int i = 0; i < idx; i++) {
if (toLower(words[i]) == toLower(hold)) {
frequency[i]++;
isFound = true;
break;
}
}
if (!isFound) {
words[idx] = hold;
frequency[idx] = 1;
idx++;
}
isFound = false;
}
First, to address your code, this is what it should probably look like. Note how we only increment i as we add words, and we only ever scan the words we've already added for duplicates. Note also how the first pass will skip the j-loop entirely and simply insert the first word with a frequency of 1.
void addWords(const std::string& fname, int count, string *words, int *frequency)
{
std::ifstream file(fname);
std::string hold;
int i = 0;
while (i < count && (file >> hold))
{
int j = 0;
for (; j<i; ++j)
{
if (toLower(words[j]) == toLower(hold))
{
// found a duplicate at j
++frequency[j];
break;
}
}
if (j == i)
{
// didn't find a duplicate
words[i] = hold;
frequency[i] = 1;
++i;
}
}
}
Second, to really address your code, this is what it should actually look like:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
//
// Your implementation of toLower() goes here.
//
typedef std::map<std::string, unsigned int> WordMap;
WordMap addWords(const std::string& fname)
{
WordMap words;
std::ifstream inf(fname);
std::string word;
while (inf >> word)
++words[toLower(word)];
return words;
}
If it isn't obvious by now how a std::map<> makes this task easier, it never will be.
check out SEEK_CUR(). If you want to set the cursor back
The problem is a logical one, consider several situations:
Your algorithm does not find the current word. It is inserted at position i of your arrays.
Your algorithm does find the word. The frequency of the word is incremented along with i, which leaves you with blank entries in your arrays whenever there's a word which is already present.
To conclude, 1 works as expected but 2 doesn't.
My advice is that you don't rely on for loops to traverse the string but use a "get-next-until-end" approach which uses a while loop. With this, you can track your next insertion point and thus get rid of the blank entries.
int currentCount = 0;
while (file)
{
// your inner for loop
if (!found)
{
*(words + currentCount) = hold;
*(frequency + currentCount) = 1;
currentCount++;
}
}
Why not use a std::map?
void collect( std::string name, std::map<std::string,int> & freq ){
std::ifstream file;
file.open(name.c_str(), std::ifstream::in );
std::string word;
while( true ){
file >> word; // add toLower
if( file.eof() ) break;
freq[word]++;
}
file.close();
}
The problem with your solution is the use of count in the inner loop where you look for duplicates. You'll need another variable, say nocc, initially 0, used as limit in the inner loop and incremented whenever you add another word that hasn't been seen yet.