【问题标题】:Rotate a string in c++?在c ++中旋转字符串?
【发布时间】:2010-10-21 19:48:00
【问题描述】:

我正在寻找一种在 C++ 中旋转字符串的方法。我把所有的时间都花在了 python 上,所以我的 c++非常生锈了。

这是我想要它做的事情:如果我有一个字符串 'abcde',我希望它更改为 'bcdea'(第一个字符移到末尾)。 这是我在 python 中的做法:

def rotate(s):
    return s[1:] + s[:1]

我不确定如何在 cpp 中执行此操作。也许使用一个字符数组?

【问题讨论】:

    标签: c++ string


    【解决方案1】:

    我推荐std::rotate

    std::rotate(s.begin(), s.begin() + 1, s.end());
    

    【讨论】:

    • C++ 标准中的第 25.2.10 节指定 std::rotate
    • 顺便说一下,如果你需要一个右旋转,使用反向迭代器:std::rotate(s.rbegin(), s.rbegin() + 1, s.rend());并且字符串变为“eabcd”
    • 也是一个仅供参考的std::rotate 可能比滚动您自己的实现要快得多。因此,如果您想确保正确性和性能,那么使用std::rotate 也是不错的选择。见stackoverflow.com/questions/21160875/why-is-stdrotate-so-fast
    【解决方案2】:

    这是一个将第一个字符“浮动”到字符串末尾的解决方案,有点像冒泡排序的单次迭代。

    #include <algorithm>
    
    string rotate(string s) {
      for (int i = 1; i < s.size(); i++)
        swap(s[i-1], s[i]);
      return s;
    }
    

    如果你想让函数就地旋转字符串:

    #include <algorithm>
    
    void rotate(string &s) {
      for (int i = 1; i < s.size(); i++)
        swap(s[i-1], s[i]);
    }
    

    【讨论】:

    • 注意一件事:如果 s.size() == 0,那么 s.size()-1 会变成 4294……因为 s.size() 是无符号的。
    【解决方案3】:

    这里有一个比较简单的方法:

    void RotateStringInPlace(char buffer[])
    {
        // Get the length of the string.
    
        int len  = strlen(buffer);
        if (len == 0) {
            return;
        }
    
        // Save the first character, it's going to be overwritten.
    
        char tmp = buffer[0];
    
        //  Slide the rest of the string over by one position.
    
        memmove(&buffer[0], &buffer[1], len - 1);
    
        // Put the character we saved from the front of the string in place.
    
        buffer[len - 1] = tmp;
        return;
    }
    

    请注意,这将修改缓冲区。

    【讨论】:

    • 我会说这更像是 C 而不是 C++,不是吗?
    • +1 用于使用 memmove 而不是 memcpy(当字符串可能重叠时这是不安全的)。
    • 我想你可以说这是比 C++ 更多的 C,但它仍然是有效的 C++,而且它可能比 std::rotate 更有效,后者的字符数为 O(n)字符串,而这种方法可能更像是 O(m),其中 m 是 n/(您平台上单词的大小)。
    • @Eric Melski:您的“O(m)”与 O(n) 相同,只是更快。大 O 表示法仅衡量算法的可扩展性,而不是实现的速度。
    【解决方案4】:

    在算法标头中有一个标准的rotate 函数。
    如果您想自己执行此操作,可以尝试以下操作:

    #include <iostream>
    #include <string>
    
    std::string rotate_string( std::string s ) {
        if (s.empty()) return s;
    
        char first = s[0];
    
        s.assign(s, 1, s.size() - 1);
        s.append(1, first);
    
        return s;
    }
    
    int main() {
        std::string foo("abcde");
    
        std::cout << foo << "\t" << rotate_string(foo) <<  std::endl;
    
        return 0;
    }
    

    当然,这里最好使用标准库,而且在大多数情况下。

    EDIT #1 我刚刚看到了 litb 的回答。再次击败!
    EDIT #2 我只想提一下,rotate_string 函数在长度为 0 的字符串上失败。您将收到 std::out_of_range 错误。您可以使用简单的 try/catch 块来解决此问题,或使用 std::rotate :-)
    EDIT #3 如果字符串的长度为 0,则返回相同的字符串。

    【讨论】:

      【解决方案5】:

      如果您不想要这些就地解决方案,那么您的 python 代码可以直接翻译成 C++,并使用一些额外的代码来处理索引越界在 C++ 中是个坏消息这一事实。

      s[1:] --> s.substr(1);
      s[:1] --> s[0]; // s[0] is a char not a string, but that's good enough
      

      所以,

      std::string rotate(const std::string &s) {
          if (s.size() > 0) return s.substr(1) + s[0];
          return s;
      }
      

      这不是最有效的:它几乎肯定会创建比最低限度的更多的字符串。但是你通常不需要最高效的,如果你想在没有不必要分配的情况下进行连接,你有reserveappend

      【讨论】:

        【解决方案6】:

        是否有要求?

        如果不是,您最好取除第一个字符以外的所有字符的子字符串,然后将第一个字符附加到末尾。

        【讨论】:

          【解决方案7】:

          在某个时候,我痴迷于分而治之,并使用了以下

          由于它将问题“划分”为更小的问题,我的猜测是它的性能更好。 欢迎访问复杂性和内存访问行为方面的专家 :)。

          Initial Call:
          rotate_about(rots_g, 0, i, j - 2);
          
          
          void rotate_about(char *str, int start, int pivot, int end)
          {
              if(pivot == start)
              {
                  return ;
              }
              else if((pivot - start) <= (end - pivot))
              {
                  int move_bytes = pivot-start;
                  swap_bytes(&str[start], &str[pivot],move_bytes);
                  rotate_about(str,pivot,pivot+move_bytes,end);
              }
              else
              {
                  int move_bytes = end - pivot + 1;
                  swap_bytes(&str[start], &str[pivot],move_bytes);
                  rotate_about(str, start+move_bytes ,pivot,end);
              }
          }
          

          【讨论】:

            【解决方案8】:

            这是不使用任何外部函数的 C 代码: 无论多大,它都会将字符串向前和向后旋转任意值。

            int stringRotate(int value)
              {
              unsigned long   I,J,K;
              unsigned long   index0;
              unsigned long   temp1,temp2;
              unsigned long   length;
            
              length = stringLength;
            
              if (value < 0)
                value = length - ((0 - value) % length);
            
              if (value > length)
                value = value % length;
            
              J = 0;
              index0 = J;
              temp1 = stringData[J];
            
              for (I = 0;I < length;I++)
                {
                K = (J + value) % length;
                temp2 = stringData[K];
                stringData[K] = temp1;
            
                J = K;
            
                temp1 = temp2;
            
                if (J == index0)
                  {
                  J++;
                  index0 = J;
                  temp1 = stringData[J];
                  }
                }
            
              return 1;
              }
            

            向前和向后旋转字符串会有点繁琐,所以最好只进行向前旋转并计算正确的向后旋转值。另外,如果旋转值大于字符串的长度,那么我们可以剪掉它,因为无论如何结果都是一样的。

            value = length - ((0 - value) % length) :表示如果旋转值为负,则将该值设置为字符串的长度,减去该值除以长度的余数的正数的字符串。例如:将长度为 10 的字符串旋转 -9 位置与旋转 +1 位置相同。将相同的字符串旋转 -19 个位置也与旋转加一相同。 value = value % length :表示如果正值大于字符串长度,则除以字符串长度并取余数。结果就像我们只是做了很长的路一样。

            要进行适当的旋转,我们需要跳转旋转的值来交换相距很远的字符。我们从零位置开始,向前移动旋转值并继续跳跃该量。如果我们越过字符串的末尾,我们就简单地回到开头。问题是,如果该值是偶数,我们将在我们开始的地方结束,并且会错过所有奇数字符。变量 index0 用于指示我们从哪里开始。如果我们最终回到这个索引,那么我们需要向前移动一个索引位置并继续跳跃。我们继续这样做,直到所有字符都被交换 此时,我们需要两个临时变量来进行就地交换。 J 是起始位置。我们将索引 J 处的字符移动到第一个临时变量。现在我们将 I 循环到字符串的长度。 K 是目标索引,J 加上旋转值,如果需要,环绕在末端。将索引 K 处的字符移动到第二个临时变量。使用第一个临时变量将索引 J 中的字符放入索引 K。顺便说一句,我们不直接将字符从索引 J 移动到索引 K 的原因是因为循环的最后一部分,索引可能在循环之间发生变化,但 temp1 中的字符不应该。 现在我们用 temp2 交换 temp1。 最后一部分是针对值是偶数的地方,我们回到了开始的地方。这将通过乘以旋转值减一来发生。将索引 J 加一并重置起始值。循环直到完成。

            可以在此处找到视频演示:https://www.youtube.com/watch?v=TMzaO2WzR24

            【讨论】:

              【解决方案9】:

              这段代码对我有用:

              using namespace std;
              
              string StrRotate(string &s, int nLeft)
              {
                   int size = s.size();
              
                   nLeft %= size;
              
                   if(nLeft == 0) return s;
              
                   string after = s.substr(0, nLeft);
                   string before = s.substr(nLeft, size - nLeft);
              
                   return before + after;
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-07-14
                • 1970-01-01
                • 1970-01-01
                • 2016-05-23
                • 2021-11-18
                • 1970-01-01
                • 2022-11-03
                • 1970-01-01
                相关资源
                最近更新 更多