代码随想录Day8

72 阅读3分钟

344.反转字符串

力扣题目链接

文章讲解

字符串基础操作! | LeetCode:344.反转字符串

// 普通交换
    void reverse(char &lhs, char &rhs) {
        char tmp = lhs;
        lhs = rhs;
        rhs = tmp;
    }
    // 位运算交换
    // 1.任意一个变量X与其自身进行异或运算,结果为0,即X^X=0
    // 2.任意一个变量X与0进行异或运算,结果不变,即X^0=X
    // 3.异或运算具有可结合性,即a^b^c=(a^b)^c=a^(b^c)
    // 4.异或运算具有可交换性,即a^b=b^a
    void bitReverse(char &lhs, char &rhs) {
        // 令a=lhs,b=rhs;
        // a = a ^ b;
        lhs ^= rhs;
        // b = a ^ b;其中a = a ^ b
        // b = a ^ b ^ b; 即b = a;
        rhs ^= lhs;
        // a = a ^ b; 其中a = a ^ b; b = a;
        // a = a ^ b ^ a; a = b;
        lhs ^= rhs;
    }
    // 时间复杂度: O(n)
    // 空间复杂度: O(1)
    void reverseString(vector<char>& s) {
        int left = 0;
        int right = s.size() - 1;
        while (left < right) {
            bitReverse(s[left], s[right]);
            ++left;
            --right;
        }
    }

541. 反转字符串II

力扣题目链接

文章讲解

字符串操作进阶! | LeetCode:541. 反转字符串II

// 使用reverse库函数版本
    // 让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否有需要反转的区间。
    // O(n) O(1)
    string reverseStr1(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 每隔2k个字符,把这个区间内的前k个字符反转
            // 剩余字符小于2k个大于k个,则反转前k个字符
            if (i + k < s.size()) {
                reverse(s.begin() + i, s.begin() + i + k);
            }
            else {
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }

    // swap实现的reverse0
    void reverse0(string &s, int start, int end) {
        for (int i = start, j = end; i < j; ++i, --j) {
            swap(s[i], s[j]);
        }
    }
    // 使用自定义reverse函数版本
    // O(n) O(1)
    string reverseStr2(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            if (i + k < s.size()) {
                reverse0(s, i, i + k - 1);
            }
            else {
                reverse0(s, i, s.size() - 1);
            }
        }
        return s;
    }

    // other
    // O(n) O(1)
    string reverseStr3(string s, int k) {
        int n = s.size();
        int pos = 0;
        while (pos < n) {
            // 剩余字符串大于k
            if (pos + k < n) {
                reverse(s.begin() + pos, s.begin() + pos + k);
            }
            // 剩余字符串小于k
            else {
                reverse(s.begin() + pos, s.end());
            }
            pos += (2 * k);
        }
        return s;
    }

题目:剑指Offer 05.替换空格

力扣题目链接

文章讲解

替换空格.gif

思路:见代码注释。

    // 双指针法,先扩充数组,再从后往前遍历。
    // 好处:不用申请新数组,避免了数组元素的移动。
    string replaceSpace(string s) {
        // 统计' '的个数
        int blankCount = 0;
        int oldSize = s.size();
        for (int i = 0 ;i < oldSize; ++i) {
            // 单引号而不是双引号
            if (s[i] == ' ') {
                ++blankCount;
            }
        }
        // 扩充数组, ' '——>'%20'
        s.resize(oldSize + 2 * blankCount);
        int newSize = s.size();
        for (int i = oldSize - 1, j = newSize - 1; i < j; --i, --j) {
            if (s[i] != ' ') {
                s[j] = s[i];
            }
            else {
                s[j] = '0';
                s[j - 1] = '2';
                s[j - 2] = '%';
                j -= 2;
            }
        }
        return s;
    }

151.翻转字符串里的单词

力扣题目链接

文章讲解

字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词

思路:可以利用中的stringstream来接收和输出单词,能够简单的解决空格问题;或是把问题分解成两部分:去除空格(前,中冗余,后)和反转(整体反转后再进行单词反转)。

   class Solution {
public:
   // 法一:利用stringstream求解
   string reverseWords(string s) {
       stringstream ss;
       string res = "", tmp;
       ss << s;
       // stringstream每次从后往前输出一个word
       while (ss>>tmp) {
           // 每次往res输入" " + word
           res = " " + tmp + res;
       }
       // 如果res不为空字符串
       if (res != "") {
           // 删除字符串开头的空格
           res.erase(res.begin());
       }
       return res;
   }

   // 法二:不适用额外内存空间,保持O(1)
   // 先删除额外的空格,再倒序,思路如下:
   // 1、移除多余空格
   // 2、将整个字符串反转
   // 3、将每个单词反转

   // 遍历多余空格,erase删除
   // erase本身是O(n)操作
   // for套erase就是O(n^2)
   void removeExtraSpaces1(string& s) {
       // 删除字符串单词间的多余空格
       for (int i = s.size() - 1; i > 0; i--) {
           if (s[i] == s[i - 1] && s[i] == ' ') {
               s.erase(s.begin() + i);
           }
       }
       // 目前只存在单空格了
       // 删除字符串最后面的空格
       if (s.size() > 0 && s[s.size() - 1] == ' ') {
           s.erase(s.begin() + s.size() - 1);
       }
       // 删除字符串最前面的空格
       if (s.size() > 0 && s[0] == ' ') {
           s.erase(s.begin());
       }

   }
   // 双指针移除空格,最后resize字符串大小
   // O(n)
   void removeExtraSpaces2(string& s) {
       // 定义快慢指针
       int slow = 0, fast = 0;
       // 去掉字符串前面的空格
       while (s.size() > 0 && fast < s.size() && s[fast] == ' ') {
           fast++;
       }
       // 去掉字符串中间部分的冗余空格
       for (; fast < s.size(); ++fast) {
           if (fast - 1 > 0
               && s[fast - 1] == s[fast]
               && s[fast] == ' '
           ) 
           {
               continue;

           } else {
               s[slow++] = s[fast];
           }
       }
       // 去掉字符串后面的空格
       if (slow - 1 > 0 && s[slow - 1] == ' ') {
           s.resize(slow - 1);
       } else {
           s.resize(slow);
       }
   }

   // 快慢指针,去除所有空格然后在相邻单词之间添加空格
   void removeExtraSpaces3(string& s) {
       int slow = 0;
       for (int i = 0; i < s.size(); ++i) {
           if (s[i] != ' ') {
               if (slow != 0) {
                   s[slow++] = ' ';
               }
               while (i < s.size() && s[i] != ' ') {
                   s[slow++] = s[i++];
               }
           }
       }
       s.resize(slow);
   }
   // 反转字符串
   void reverse(string& s, int start, int end) {
       for (int i = start, j = end; i < j; i++, j--) {
           swap(s[i], s[j]);
       }
   }
   string reverseWords2(string s) {
       // 步骤一
       removeExtraSpaces3(s);
       // 步骤二
       reverse(s, 0, s.size() - 1);
       // 步骤三 反转单词
       int start = 0;
       for (int i = 0; i <= s.size(); ++i) {
           if (i == s.size() || s[i] == ' ') {
               reverse(s, start, i - 1);
               start = i + 1;
           }
       }
       return s;
   }
};

题目:剑指Offer58-II.左旋转字符串

力扣题目链接

讲解

image.png

//反转区间为前n的子串
    //反转区间为n到末尾的子串
    //反转整个字符串
    string reverseLeftWords(string s, int n) {
        //  reverse()反转 [first, last) 范围中的元素顺序
        //  左闭右开区间
        reverse(s.begin(), s.begin() + n);
        reverse(s.begin() + n, s.end());
        reverse(s.begin(), s.end());
        return s;
    }
    string reverseLeftWords2(string s, int n) {
        string sub1 = s.substr(0, n);
        string sub2 = s.substr(n, s.size() - n);
        string res = sub1 + sub2;
        return res;
    }