字符串的旋转

题目描述

给定一个字符串,要求将字符串的前若干(m)位字符移动到字符串的尾部。
**例如**:将字符串"abcdef"的前3个字符'a'、'b'和'c'移到字符串的尾部,得到"defabc"。

解法一:

n轮移动,每轮中将每个字符左移一位。

时间复杂度:O(mn); 空间复杂度:O(1)

void leftRotateString_1(string &str, int m)
{
    if (m < 0) {                // if m is negative, the modulus is implementation-defined
        m += (-m / str.size() + 1) * str.size();
    }
    m %= str.size();            // m >= str.size(), shrink the value by multiple str.size()
    for (int i = 0; i < m; ++i) {       // shift left by one in every loop
        char tmp = str[0];              // extra space O(1)
        for (int j = 0; j < str.size() - 1; ++j) {
            str[j] = str[j + 1];
        }
        str[str.size() - 1] = tmp;
    }
}

解法二:

将字符串分为两部分X("abc")、Y("def")。将源字符串全部反转后,XY两部分内容成功互换("fedcba"),但是X、Y内部也被反转;分别对X、Y内部进行反转后得到正确结果("defabc")。

时间复杂度:O(n); 空间复杂度:O(1),优化后为O(0)

void reverseString(string &str, int left, int right)
{
    while (left < right) {
        // swap two element by XOR operator to eliminate the extra space cost
        str[left] ^= str[right] ^= str[left] ^= str[right]; 
        ++left;
        --right;
    }
}
void leftRotateString_2(string &str, int m)
{
    if (m < 0) {
        m += (-m / str.size() + 1) * str.size();
    }
    m %= str.size();
    reverseString(str, 0, m - 1);               // X segment with m characters
    reverseString(str, m, str.size() - 1);      // Y segment with n characters
    reverseString(str, 0, str.size() - 1);      // overall string
}

解法三:

直接将元素循环左移m位;
具体的实现为:保存首个元素,将之后的第m个元素移动到当前位置。循环直到需要移动的元素为保存的元素;
类似于方法一种左移一位的过程,但是产生多个循环体的问题,即一轮循环后不是所有元素都被后移m位;
比如"abcdef",左移4位;一轮循环后为"efabcd",其中"ace"实现左移m位,而"bdf"保持原位;
解决方法:n个数移动m位,其中循环体的个数为n和m的最大公约数gcd,循环gcd趟得到结果(具体数学原理不知,实例演算一遍很容易得到答案)。

时间复杂度:O(n); 空间复杂度:O(1)。
相对于方法二,时间复杂度减少二分之一。因为方法二实际上每个元素移动两次,二方法三只移动一次。

int gcd(int a, int b)           // the greatest common divisor
{
    int rem;
    while (b > 0) {
        rem = a % b;
        a = b;
        b = rem;
    }
    return a;
}
void leftRotateString_3(string &str, int m)
{
    if (m < 0) {
        m += (-m / str.size() + 1) * str.size();
    }
    m %= str.size();
    int pass = gcd(m, str.size());
    for (int i = 0; i < pass; ++i) {
        // in a loop, recursively shift left element by m interval
        char tmp = str[i];
        int j;
        for (j = 0; j < str.size() / pass - 1; ++j) {
            str[(i + j * m) % str.size()] = str[(i + j * m + m) % str.size()];
        }
        str[(i + j * m) % str.size()] = tmp;
    }
}

完整测试代码:

#include <iostream>
#include <string>
using namespace std;
void leftRotateString_1(string &str, int m)
{
    if (m < 0) {                // if m is negative, the modulus is implementation-defined
        m += (-m / str.size() + 1) * str.size();
    }
    m %= str.size();            // m >= str.size(), shrink the value by multiple str.size()
    for (int i = 0; i < m; ++i) {       // shift left by one in every loop
        char tmp = str[0];              // extra space O(1)
        for (int j = 0; j < str.size() - 1; ++j) {
            str[j] = str[j + 1];
        }
        str[str.size() - 1] = tmp;
    }
}
void reverseString(string &str, int left, int right)
{
    while (left < right) {
        // swap two element by XOR operator to eliminate the extra space cost
        str[left] ^= str[right] ^= str[left] ^= str[right]; 
        ++left;
        --right;
    }
}
void leftRotateString_2(string &str, int m)
{
    if (m < 0) {
        m += (-m / str.size() + 1) * str.size();
    }
    m %= str.size();
    reverseString(str, 0, m - 1);               // X segment with m characters
    reverseString(str, m, str.size() - 1);      // Y segment with n characters
    reverseString(str, 0, str.size() - 1);      // overall string
}
int gcd(int a, int b)           // the greatest common divisor
{
    int rem;
    while (b > 0) {
        rem = a % b;
        a = b;
        b = rem;
    }
    return a;
}
void leftRotateString_3(string &str, int m)
{
    if (m < 0) {
        m += (-m / str.size() + 1) * str.size();
    }
    m %= str.size();
    int pass = gcd(m, str.size());
    for (int i = 0; i < pass; ++i) {
        // in a loop, recursively shift left element by m interval
        char tmp = str[i];
        int j;
        for (j = 0; j < str.size() / pass - 1; ++j) {
            str[(i + j * m) % str.size()] = str[(i + j * m + m) % str.size()];
        }
        str[(i + j * m) % str.size()] = tmp;
    }
}
int main()
{
    string str = "abcdef";
    leftRotateString_3(str, -2);
    cout << str << endl;

    return 0;
}

举一反三

单词翻转
输入一个英文句子,反转句子中单词的顺序。例如,输入为"I am a student.",则输出"student. a am I"。

解法:类似解法二,将每个单词翻转,再将整个句子以字母为单位翻转。

#include <iostream>
#include <string>
using namespace std;
class Solution {
public:
    string ReverseSentence(string str) {
        if (str.length() == 0) {
            return str;
        }
        // reverse every word
        int i = 0, j = 0;
        while (true) {
            while (i < str.length() && str[i] == ' ') {
                ++i;
            }
            if (i == str.length()) {
                break;
            }
            j = i;
            while (j < str.length() && str[j] != ' ') {
                ++j;
            }
            reverseString(str, i, j - 1);
            i = j;
        }
        // reverse overall sentence
        reverseString(str, 0, str.length() - 1);
        return str;
    }
private:
    void reverseString(string &str, int left, int right) {
        while (left < right) {
            str[left] ^= str[right] ^= str[left] ^= str[right];
            ++left;
            --right;
        }
    }
};
int main()
{
    Solution sol;
    string str("I'm camel, not a camel.");
    string ret = sol.ReverseSentence(str);
    cout << ret << endl;

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值