day8 | 字符串的各种反转操作

100 阅读2分钟

344.反转字符串

题目链接:leetcode.cn/problems/re…

要点

  • swap不需要include文件,reverse要引入algorithm文件

双指针

class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int i=0, j=s.size()-1; i<j; i++, j--) {
            swap(s[i], s[j]);
        }
    }
};

总结

头尾两个指针相向移动,互换对应位置的元素

541.反转字符串II

题目链接:leetcode.cn/problems/re…

要点

  • i, i+2k都是某个2k组的第一个元素,所以s.begin()+i+k不用再+1

模拟

class Solution {
public:
    string reverseStr(string s, int k) {

        for (int i = 0; i < s.length(); i += (2*k)) {
            // 当前这个2k组的前k个都在
            if (i+k < s.length()) reverse(s.begin()+i, s.begin()+i+k);   //--
            else {
                // 否则只翻转剩下的元素
                reverse(s.begin()+i, s.end());
            }
        }
        return s;
    }
};

总结

这是一道模拟题,根据题目的意思设置跨度为2k的指针i,指向某2k组的第一个元素。再根据两条规则翻转字符串

剑指 Offer 05. 替换空格

题目链接:leetcode.cn/problems/ti…

要点

  • 双指针:这题和27.移除元素思路相似,两个指针指向新老两个数组的对应位置,在一个for循环里做了两个循环做的事
  • 后序:从后往前修改,避免了从前往后每次插入新元素要整体移动后面的元素,而且最后几个元素为resize出来的空元素
  • ntail指向新字符串的需要更新的位置,otail指向旧字符串的检索到的位置
  • 先扩容再操作

双指针

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0;
        int otail = s.size()-1;
        for (int i = 0; i < s.size(); i++){
            count = s[i] == ' ' ? count+1 : count;
        }

        s.resize(s.size() + 2*count);
        int ntail = s.size()-1;
        
        for (otail; otail >= 0; otail--) {
            if (s[otail] != ' ') s[ntail--] = s[otail];
            else {
                s[ntail--] = '0';
                s[ntail--] = '2';
                s[ntail--] = '%';
            }
        }
        return s;
    }   
};

总结

字符串的resize不改变其capacity大小(默认15),只改变size大小,先扩容将存%20多余字符的空间开出来。

151.反转字符串中的单词

题目链接:leetcode.cn/problems/re…

要点

  • 先用双指针法清洗一下字符串,将多余的空格舍弃,重新设置string.size
  • 再整体反转字符串,单词反转,转回单词正常顺序

双指针 + 反转字符串

class Solution {
public:
    void reverse(string& s, int start, int end) {
        for (start, end; start < end; start++, end--) {
            swap(s[start], s[end]);
        }
    }

    void removeBlank(string& s) {
        int slow = 0;
        //fast指针去找非空格字符
        for (int fast = 0; fast < s.size(); fast++) {

            if (s[fast] != ' ') {
                s[slow++] = s[fast++];

                //填完当前这个单词
                while (fast < s.size() && s[fast] != ' ') s[slow++] = s[fast++];
                //在单词后补个空格,始终指向新字符串下一个待填的位置
                s[slow++] = ' ';
            }
        }
        s.resize(slow-1);
    }

    string reverseWords(string s) {
        removeBlank(s);
        reverse(s, 0, s.size()-1);

        int i = 0;
        for (int j = 0; j <= s.size(); j++) {
            //只有快的那个指针遇到空格 或者 到字符串末尾了,才将这个单词再反一下
            if (j == s.size() || s[j] == ' ') {
                 reverse(s, i, j-1);
                 i = j+1;
            }
        }
        return s;
    }
};

总结

这题和上一题的双指针法都有移除元素的味道,快慢指针分别指向新老字符串的对应位置,快指针++写在for循环条件里,循环体里通常只有一个if判断,不满足就fast++找下一个了。

for (int fast = 0; fast < s.size(); fast++) {
    if (s[fast] != ' ') {
        //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        s[slow++] = s[fast++];
        //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    }
}

清洗字符串和反转字符串子串单词都用到了类似的双指针法

剑指 Offer 58 - II. 左旋转字符串

题目链接:leetcode.cn/problems/zu…

要点

  • 左旋、右旋有点像队列的出队入队
  • 先将两个子串反转,再整体反转(反过来也可以,只不过分割点在对称到另一侧了)

双指针

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(), s.begin()+n);
        reverse(s.begin()+n, s.end());
        reverse(s.begin(), s.end());

        return s;
    }
};

总结

右旋同理,本质是两个子串交换一下位置

reverse函数左闭右开,时间复杂度O(n)、空间复杂度O(1)