简体   繁体   中英

Recursive function that reverses substring within a string

I am working on a lab assignment where the user inputs a string and a starting and stopping point for a substring within the string to be reversed. For example, if a user inputs the string "go bobcats", and the numbers 3 (for starting index) and 7 (for ending index), the output should be "go acbobts". I was able to write a recursive function that reverses an entire string ("go bobcats" becomes "stacbob og"), but I am having trouble with the substring aspect.

code for full string reverse:

void reversing(string s, int start, int end){
    if(s.size() == 0){return;}
    else{
        reversing(s.substr(1), start + 1, end);
        cout << s[0];
    }
}

For the starting and ending index for this I just entered 0 and 9 because that would be the full length of the string.

How can I adjust the function so that it only reverses the string starting and ending at the indexes the user inputs? Also, with my current function I have to use an endl in the main to make a new line at the end of the output of the string. Is there a way I can do this inside the function? If I put an endl after cout << s[0]; it puts in a new line after each iteration making the output vertical:

s

t

a

c

b

o

b

o

g

Implementation in main:

string s;
    int start, end;
    cout << "Enter a string: ";
    while(cin.peek() == '\n' || cin.peek() == '\r'){
        cin.ignore();
    }
    getline(cin,s);
    cout << "Now enter two numbers that are within the bounds of the string. ";
    cin >> start >> end;
    cout << "This is how your words look now:\n";
    reversing(s,start,end);
    cout << endl;

A function to reverse the string can swap the elements at both ends of the range and decrease the range by one on both sides.

void reversing(string& s, int start, int end) {
    if (start >= end)
        return;
    swap(s[start], s[end]);
    reversing(s, start + 1, end - 1);
}

And then inside main() :

// ...
cout << "This is how your words look now:\n";
reversing(s, start, end);
cout << s << endl;

Sometimes it is sad to see how C++ is used to teach everything but not C++. The following is sort of an experiment to see if we can somehow approach std::reverse (the algorithm you should actually use) by actually ignoring the requirements of your homework and doing small digestable steps.

Lets start with a small variation on the solution presented in this answer . Instead of passing the string together with indices we can use iterators. In a nutshell, iterators are the glue between algorithms and data structures, more specifically container . They can refer to elements in a container, just like an index or pointer can do.

void reversing2(std::string::iterator first, std::string::iterator last) {
    if (first >= last) return;
    std::swap(*first,*last);
    reversing2(++first,--last);
} 

Iterators can be dereferenced like pointers to get a reference to the element ( *first and *last ). RandomAccessIterators can be incremented ( ++first ), decremented ( --last ) and be compared ( first >= last ), just like you would do it with indices.

The next step is a difficult one, because it requires even more handwaving. Note that apart from the function signature nothing in the above function actually depends on first and last being iterators for elements in a std::string . For example to reverse a subarray of an int[] only the signature would have to change:

void reversing2(int* first, int* last) {
    if (first >= last) return;
    std::swap(*first,*last);
    reversing2(++first,--last);
}

That makes a nice opportunity to get in touch with templates. I know that I am commiting a small crime here, because I cannot give a thorough intro, but will only present you a very narrow case. To make the same code usable for different containers we just have to modify it a little

template <typename IT>
void reversing(IT first,IT last) {
    if (first >= last) return;
    std::swap(*first,*last);
    reversing(++first,--last);
}

This can now be called with any RandomAccessIterator. So this:

#include <string>
#include <iostream>
int main() {        
   std::string s{"Hello world"};       
   std::cout << s << '\n';
   reversing2(s.begin()+3,s.begin()+7);    // pass iterators to 4th and 8th character
   std::cout << s << '\n';
   reversing(s.begin()+3,s.begin()+7);
   std::cout << s << '\n';   
   int x[]= {1,2,3,4,5,6};
   reversing(&x[2],&x[5]);                 // pointers are iterators too
   for (const auto e : x) std::cout << e;
}

Will produce this output:

Hello world
Helow olrld
Hello world
126543

Eventually, and this was the whole motivation for the preceding, we can see that the reversing is quite similar to std::reverse . Of course std::reverse is not recursive and there is one small caveat: standard algorithms typically work on half-open intervals, ie a range made from two iterators first and last where first is included in the interval, but last is one past the last element in the interval. Hence to get the same result, you would have to call it with the second iterator one position further than with the above function:

std::reverse(s.begin()+3,s.begin()+8);  // pass iterators to 4th and one past the 8th character

Complete online example

In your function declaration the type of the first parameter is not a referenced type. So the function deals with a copy of an original string passed to the function as an argument.

However in any case your recursive function definition is invalid. At least there is no need to extract a sub-string.

Pay attention to that the second and the third parameters of the function should have the type std::string::size_type . Otherwise the user can supply a negative values for the parameters when they have the type int and the function will have undefined behavior.

Also it is better when the function returns reference to the reversed string itself.

In fact within the function you need to use only one check that the start position is less than the end position.

Here is a demonstrative program that shows how the function can be defined.

#include <iostream>
#include <string>

std::string & reversing( std::string &s, std::string::size_type start, std::string::size_type end )
{
    if ( not s.empty() )
    {
        if ( not ( end < s.size() ) ) end = s.size() - 1;

        if ( start < end )
        {
            std::swap( s[start], s[end] );
            reversing( s, start + 1, end - 1 );
        }
    }       

    return s;
}

int main() 
{
    std::string s( "Hello bobaloogie" );

    std::cout << s << '\n';
    std::cout << reversing( s, 0, 4 ) << '\n';
    std::cout << reversing( s, 6, s.size() ) << '\n';

    return 0;
}

The program output is

Hello bobaloogie
olleH bobaloogie
olleH eigoolabob

well i also have a solution but without implementing library function just to give you a feel of implementation and it's pretty simple.

  1. adjusting your function - swap the start and last position recursively instead of doing exhaustively.

    1. endl in the main - if you wish to save the answer in input string only then yes you have to do that in main. Else just before returning from function put ' endl '.

My code goes like this.

#include<bits/stdc++.h>
using namespace std;


void rev(string &str,int s,int l){ // s = start l = last
     if(l<s) return ;            // base condition when l precedes s
     else {
         char temp = str[s];
         str[s] = str[l];
         str[l] = temp;
         rev(str,++s,--l);
     }
     return ;           
}

int main(){
   string str;
   int s,l;
   getline(cin,str);
   cin>>s>>l;
   assert(s<str.size() && l<str.size());
   rev(str,s,l);
   cout<<str;
} 

There are different way to approach this problem, the greedy one is to use substring to pass the exact string to the reverse function:

void reversing(string s){
    if(s.size() == 0){return;}
    else{
        reversing(s.substr(1));
        cout << s[0];
    }
}

void main(string s, int start, int end) {
    string substring = reversing(s.substr(start, end - start + 1));
    cout << s.substr(0, start) + substring + s.substr(end + 1);
}

else you need to edit your function that reverse to edit the string only when in such range

void reversing(string s, int start, int end, int index = 0, string output = ""){
    if(s.length() == index){return output;}
    else{
        if (index >= start && index <= end) {
            output = output + s[end - (index - start)];
        } else {
            output += s[index];
        }
        reversing(s, start, end, index+1, output);
        cout << output[output.length()-1];
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM