Рекурсивная функция, которая переворачивает подстроку в строке - PullRequest
0 голосов
/ 14 апреля 2020

Я работаю над лабораторным заданием, в котором пользователь вводит строку, а также точку начала и остановки для подстроки в строке, которую нужно изменить. Например, если пользователь вводит строку «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;

Ответы [ 5 ]

2 голосов
/ 14 апреля 2020

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

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;
1 голос
/ 14 апреля 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 не является рекурсивным, и есть одна небольшая оговорка: стандартные алгоритмы обычно работают с полуоткрытыми интервалами, ie диапазон, составленный из двух итераторов first и last, где first включено в интервал , но last - один за последним элементом в интервале. Следовательно, чтобы получить тот же результат, вам придется вызывать его со вторым итератором на одну позицию дальше, чем с вышеуказанной функцией:

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

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

0 голосов
/ 14 апреля 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];
    }
}
0 голосов
/ 14 апреля 2020

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

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

  2. endl в основном - если вы хотите sh сохранить ответ только во входной строке, тогда да, вы должны сделать это в основном. В противном случае перед возвратом из функции введите '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;
} 
0 голосов
/ 14 апреля 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
...