Рекурсивная функция, переворачивающая подстроку в строке

Я работаю над лабораторным заданием, где пользователь вводит строку, а также начальную и конечную точки для подстроки в строке, которую нужно перевернуть. Например, если пользователь вводит строку "go bobcats" и числа 3 (начальный индекс) и 7 (конечный индекс), вывод должен быть "go acbobts". Мне удалось написать рекурсивную функцию, которая переворачивает всю строку ("go bobcats" становится "stacbob og"), но у меня возникли проблемы с аспектом подстроки.

код для полного реверса строки:

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

Для начального и конечного индекса для этого я просто ввел 0 и 9, потому что это будет полная длина строки.

Как я могу настроить функцию так, чтобы она меняла только строку, начинающуюся и заканчивающуюся индексами, которые вводит пользователь? Кроме того, с моей текущей функцией я должен использовать endl в основном, чтобы создать новую строку в конце вывода строки. Есть ли способ сделать это внутри функции? Если я поставлю endl после cout << s[0];, он вставит новую строку после каждой итерации, делая вывод вертикальным:

s

t

a

c

b

o

b

o

g

Реализация в основном:

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;

person bobaloogie    schedule 14.04.2020    source источник
comment
Должен ли он быть рекурсивным?   -  person 0x499602D2    schedule 14.04.2020
comment
Вы можете найти полезным std::reverse.   -  person Jesper Juhl    schedule 14.04.2020
comment
@ 0x499602D2 да, это должно быть рекурсивно   -  person bobaloogie    schedule 14.04.2020
comment
Предоставьте минимально воспроизводимый пример. В коде, который вы разместили, нет endl   -  person 463035818_is_not_a_number    schedule 14.04.2020
comment
Не похоже, что вы получаете много пользы от end.   -  person user4581301    schedule 14.04.2020
comment
@idclev463035818 добавил основную реализацию   -  person bobaloogie    schedule 14.04.2020
comment
Почему бы вам просто не передать часть подстроки вашей функции, которую нужно изменить? И печатать две другие подстроки нормально?   -  person ChasedByDeath    schedule 14.04.2020
comment
@SadmanSakib Нам дан прототип функции и основная для наших лабораторий   -  person bobaloogie    schedule 14.04.2020
comment
@babaloogie, лучше быть точным в отношении таких ограничений в вопросе, потому что вопрос о домашнем задании может иметь самые странные требования (например, отсутствие использования std::reverse - это то, что я бы назвал странным;)   -  person 463035818_is_not_a_number    schedule 14.04.2020
comment
@babaloogie Задача невозможна с заданным прототипом. Функция ничего не возвращает (т. е. void) и передает все по значению, поэтому функция с этим прототипом вообще ничего не может вернуть.   -  person john    schedule 14.04.2020
comment
@john Он печатал в самой функции. Так что просто передача подстроки, которую нужно перевернуть, может сделать работу. Но да, это сделало бы данные диапазоны недействительными в качестве входных данных. Я так понимаю, что это не решение.   -  person ChasedByDeath    schedule 14.04.2020


Ответы (5)


Функция обращения строки может поменять местами элементы на обоих концах диапазона и уменьшить диапазон на единицу с обеих сторон.

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

А затем внутри main():

// ...
cout << "This is how your words look now:\n";
reversing(s, start, end);
cout << s << endl;
person 0x499602D2    schedule 14.04.2020
comment
Спасибо! Думаю, я был ближе к решению, чем думал, потому что изначально пробовал что-то похожее на это. Однако один вопрос: почему это не работает, если строка не передается по ссылке? - person bobaloogie; 14.04.2020
comment
@bobaloogie Передача по значению (без &) создает копию, поэтому передается не исходный объект, а копия, локальная для функции. - person 0x499602D2; 14.04.2020
comment
Попался, имеет смысл. - person bobaloogie; 14.04.2020

Иногда грустно видеть, как C++ используется для обучения всему, но не C++. Ниже приведен своего рода эксперимент, чтобы увидеть, можем ли мы как-то приблизиться к std::reverse ( алгоритм, который вы действительно должны использовать), фактически игнорируя требования вашей домашней работы и выполняя небольшие усваиваемые шаги.

Начнем с небольшой вариации решения, представленного в этом ответе. Вместо передачи string вместе с индексами мы можем использовать итераторы. Короче говоря, итераторы являются связующим звеном между алгоритмы и структуры данных, в частности контейнер. Они могут ссылаться на элементы в контейнере точно так же, как индекс или указатель.

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

Итераторы можно разыменовывать как указатели, чтобы получить ссылку на элемент (*first и *last). RandomAccessIterators можно увеличивать (++first), уменьшать (--last) и сравнивать (first >= last), точно так же, как вы делаете это с индексами.

Следующий шаг труден, потому что требует еще большего махания руками. Обратите внимание, что кроме сигнатуры функции ничто в приведенной выше функции на самом деле не зависит от того, что first и last являются итераторами для элементов в std::string. Например, чтобы изменить подмассив int[], должна измениться только подпись:

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

Это дает прекрасную возможность познакомиться с шаблонами. Я знаю, что совершаю здесь маленькое преступление, потому что не могу дать подробного вступления, а представлю вам лишь очень узкий случай. Чтобы один и тот же код можно было использовать для разных контейнеров, нам просто нужно его немного изменить.

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

Теперь это можно вызывать с помощью любого RandomAccessIterator. Итак, это:

#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;
}

Будет производить этот вывод:

Hello world
Helow olrld
Hello world
126543

В конце концов, и это было всей мотивацией для предыдущего, мы можем видеть, что reversing очень похож на std::reverse. Конечно, std::reverse не является рекурсивным, и есть одно небольшое предостережение: стандартные алгоритмы обычно работают с полуоткрытыми интервалами, т. е. диапазоном, составленным из двух итераторов first и last, где first включено в интервал, но last находится после последнего элемента в интервале. интервал. Следовательно, чтобы получить тот же результат, вам пришлось бы вызывать его со вторым итератором на одну позицию дальше, чем с вышеупомянутой функцией:

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

Полный онлайн-пример

person 463035818_is_not_a_number    schedule 14.04.2020

В объявлении вашей функции тип первого параметра не является ссылочным типом. Таким образом, функция имеет дело с копией исходной строки, переданной функции в качестве аргумента.

Однако в любом случае ваше определение рекурсивной функции неверно. По крайней мере, нет необходимости извлекать подстроку.

Обратите внимание, что второй и третий параметры функции должны иметь тип std::string::size_type. В противном случае пользователь может указать отрицательные значения для параметров, если они имеют тип int, и функция будет иметь неопределенное поведение.

Также лучше, когда функция возвращает ссылку на саму перевернутую строку.

Фактически внутри функции вам нужно использовать только одну проверку того, что начальная позиция меньше конечной.

Вот демонстрационная программа, показывающая, как можно определить функцию.

#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;
}

Вывод программы

Hello bobaloogie
olleH bobaloogie
olleH eigoolabob
person Vlad from Moscow    schedule 14.04.2020

ну, у меня также есть решение, но без реализации библиотечной функции, просто чтобы дать вам представление о реализации, и это довольно просто.

  1. корректировка вашей функции – рекурсивно поменяйте местами начальную и последнюю позиции, а не полностью .

    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 '.

Мой код выглядит так.

#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;
} 
person Adrenaleontechie    schedule 14.04.2020

Есть разные способы решения этой проблемы, самый жадный — использовать подстроку для передачи точной строки обратной функции:

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);
}

иначе вам нужно отредактировать свою функцию, которая реверсирует, чтобы редактировать строку только в таком диапазоне

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];
    }
}
person Nicola Montrone    schedule 14.04.2020