【问题标题】:Recursive function that reverses substring within a string反转字符串中的子字符串的递归函数
【发布时间】:2020-07-27 14:37:02
【问题描述】:

我正在做一个实验室作业,用户输入一个字符串以及字符串中要反转的子字符串的起点和终点。例如,如果用户输入字符串“go bobcats”,以及数字 3(用于起始索引)和 7(用于结束索引),则输出应为“go accobts”。我能够编写一个递归函数来反转整个字符串(“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,因为那将是字符串的全长。

如何调整函数,使其仅反转在用户输入的索引处开始和结束的字符串?此外,使用我当前的函数,我必须在 main 中使用 endl 在字符串输出的末尾创建一个新行。有没有办法在函数内部做到这一点?如果我在cout &lt;&lt; s[0]; 之后放置一个endl,它会在每次迭代后插入一个新行,使输出垂直:

s

t

一个

c

b

o

b

o

g

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;

【问题讨论】:

  • 一定要递归吗?
  • 您可能会发现std::reverse 很有用。
  • @0x499602D2 是的,它必须是递归的
  • 请提供minimal reproducible example。您发布的代码中没有endl
  • 看起来你对end没有太多用处。

标签: c++ string recursion reverse function-definition


【解决方案1】:

反转字符串的函数可以交换范围两端的元素,并将范围两边减一。

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;

【讨论】:

  • 谢谢!我想我比我想象的更接近解决方案,因为我最初尝试了类似的东西。但是有一个问题,如果字符串不是通过引用传递的,为什么这不起作用?
  • @bobaloogie 按值传递(没有&amp;)会创建一个副本,因此传入的不是原始对象,而是函数本地的副本。
  • 明白了,有道理。
【解决方案2】:

有时看到 C++ 被用来教授所有东西而不是 C++ 时,会感到难过。下面是一个实验,看看我们是否可以通过实际上忽略你的作业要求并做一些可消化的小步骤来以某种方式接近std::reverse(你应该实际使用的算法)。

让我们从this answer 中提出的解决方案的一个小变化开始。我们可以使用迭代器,而不是将string 与索引一起传递。简而言之,iterators 是algorithms 和数据结构之间的粘合剂,更具体地说是container。它们可以引用容器中的元素,就像索引或指针一样。

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

可以像指针一样取消引用迭代器以获取对元素的引用(*first 和 *last)。 RandomAccessIterator 可以递增 (++first)、递减 (--last) 并进行比较 (first &gt;= 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

Complete online example

【讨论】:

    【解决方案3】:

    在您的函数声明中,第一个参数的类型不是引用类型。因此,该函数处理作为参数传递给函数的原始字符串的副本。

    但是在任何情况下,您的递归函数定义都是无效的。至少不需要提取子字符串。

    注意函数的第二个和第三个参数的类型应该是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
    

    【讨论】:

      【解决方案4】:

      好吧,我也有一个解决方案,但没有实现库函数,只是为了让您有一种实现的感觉,而且非常简单。

      1. 调整你的函数 - 递归地交换开始和最后一个位置,而不是这样做 详尽无遗。

        1. endl in main - 如果您只想将答案保存在输入字符串中,那么是的,您必须在 main 中执行此操作。否则就在从函数 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;
      } 
      

      【讨论】:

        【解决方案5】:

        解决这个问题有不同的方法,贪婪的一种是使用子字符串将确切的字符串传递给反向函数:

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

        【讨论】:

          猜你喜欢
          • 2011-03-26
          • 2020-06-07
          • 1970-01-01
          • 2013-02-25
          • 1970-01-01
          • 2022-01-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多