LeetCode Day8

264 阅读3分钟

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。 示例 1: 输入:["h","e","l","l","o"] 输出:["o","l","l","e","h"] 示例 2: 输入:["H","a","n","n","a","h"] 输出:["h","a","n","n","a","H"]

思路

使用双指针技术,一个指针从数组的开始位置开始,另一个指针从数组的结束位置开始,然后交换这两个指针所指向的元素的位置,直到两个指针相遇或交叉。

题解

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

541.反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。 如果剩余字符少于 k 个,则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。 示例: 输入: s = "abcdefg", k = 2 输出: "bacdfeg"

思路

  1. 初始化:我们从s的开始位置初始化i为0。
  2. 循环条件while (i < s.size())。这个条件确保我们只在还有字符需要处理时继续循环。当i达到或超过s的长度时,循环结束。
  3. 循环体:在每次迭代中,我们根据从当前位置i到字符串结束的剩余字符数来决定如何处理字符。
  • Case 1: 剩余字符少于k个:这意味着我们没有足够的字符来完成一个完整的反转操作。在这种情况下,我们简单地反转从is结束的所有字符。
  • Case 2: 剩余字符少于2k但至少k个:这意味着我们有足够的字符来完成一个完整的反转操作,但没有足够的字符来完成下一个不反转的k个字符。所以,我们反转从i开始的k个字符,然后添加剩余的字符(不反转)。
  • Case 3: 剩余字符至少2k个:这是最简单的情况,我们反转从i开始的k个字符,然后添加下一个k个字符(不反转)。
  1. 更新迭代变量:在循环体的末尾,我们更新i的值,使其增加2k,以便在下一次迭代中处理下一个2k个字符。

题解

class Solution {
public:
    string reverseStr(string s, int k) {
        int i=0;
        while(i<s.size()){
            if(s.size()-i<k){
                reverse(s.begin()+i,s.end());
            }
            else if(s.size()-i>=k&&s.size()-i<2*k){
                reverse(s.begin()+i,s.begin()+i+k);
            }
            else{
                reverse(s.begin()+i,s.begin()+i+k);
            }
            i+=2*k;
        }
        return s;
    }
};

剑指 Offer 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 示例 1: 输入:s = "We are happy." 输出:"We%20are%20happy."

思路

这个巧妙,乍一看是很简单的题目,实际上仍然可以用双指针法使用双指针法替换字符串中的空格通常涉及两个步骤:

  1. 第一次遍历:计算字符串中空格的数量。这是为了确定替换后的字符串长度。
  2. 第二次遍历:从后向前替换空格。

具体步骤如下:

  1. 初始化两个指针:p1p2p1 指向原始字符串的末尾,p2 指向替换后的字符串的末尾。
  2. 移动 p1,并为每个空格添加两个额外的位置(因为 "%20" 比 " " 长2个字符)。
  3. p1p2 没有重叠时,从后向前移动 p1 并复制字符到 p2 的位置。
  4. p1 指向一个空格时,将 "%20" 放到 p2 指向的位置,并向后移动3个位置。否则,复制 p1 指向的字符到 p2 指向的位置,并向后移动1个位置。

题解

#include <string>

class Solution {
public:
    std::string replaceSpace(std::string s) {
        int count = 0; // 用于计算空格的数量
        for (char c : s) {
            if (c == ' ') {
                count++;
            }
        }

        int oldSize = s.size();
        s.resize(oldSize + count * 2); // 扩展字符串大小以容纳替换后的字符
        int newSize = s.size();

        int p1 = oldSize - 1, p2 = newSize - 1;
        while (p1 >= 0 && p1 < p2) {
            if (s[p1] == ' ') {
                s[p2--] = '0';
                s[p2--] = '2';
                s[p2--] = '%';
                p1--;
            } else {
                s[p2--] = s[p1--];
            }
        }
        return s;
    }
};

151.反转字符串中的单词

给定一个字符串,逐个翻转字符串中的每个单词。 示例 1: 输入: "the sky is blue" 输出: "blue is sky the" 示例 2: 输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 示例 3: 输入: "a good example" 输出: "example good a" 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

思路

这个题可以简单地用split完成,为了更好地练习字符串操作以及节省内存,我使用了“快慢指针”的技巧。

  1. 移除多余的空格:

我们定义两个指针:一个快指针 i 和一个慢指针 slow。 快指针 i 用于遍历整个字符串。 慢指针 slow 用于记录我们要写入的位置。 当遇到一个非空字符或一个单词的开始时,我们复制该单词到由慢指针 slow 指示的位置,并适当地在单词之间添加空格。这样,我们可以保证字符串中只有一个空格分隔单词,并删除开头和结尾的空格。

  1. 翻转整个字符串: 这一步很直接,我们从头到尾反转整个字符串。
  2. 逐个翻转每个单词: 由于我们已经翻转了整个字符串,所以单词的顺序已经是正确的,但每个单词内的字符顺序是反的。为了纠正这一点,我们再次遍历字符串,每次遇到一个单词就将其反转。

题解

class Solution {
public:
    // 反转字符串的一部分
    void reverse(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    // 去除多余的空格
    void removeExtraSpaces(string& s) {
    int slow = 0; // 慢指针,表示当前要写入的位置
    int i = 0;   // 快指针,用于遍历整个字符串

    while (i < s.size()) {
        // 跳过连续的空格
        while (i < s.size() && s[i] == ' ') {
            i++;
        }

        // 如果不是字符串的开始,且不是空字符串,则添加空格
        if (slow > 0 && i < s.size()) {
            s[slow++] = ' ';
        }

        // 复制当前单词
        while (i < s.size() && s[i] != ' ') {
            s[slow++] = s[i++];
        }
    }
    
    // 调整字符串的大小以删除多余的空格
    s.resize(slow);
}

    // 逐个翻转每个单词
    string reverseWords(string s) {
        removeExtraSpaces(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;
    }
};

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

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 示例 1: 输入: s = "abcdefg", k = 2 输出: "cdefgab" 示例 2: 输入: s = "lrloseumgh", k = 6 输出: "umghlrlose" 限制: 1 <= k < s.length <= 10000

思路

核心思想是利用三次反转来实现字符串的左旋转。为了更好地理解,让我们分步骤地解析它: 首先,考虑一个示例:s = "abcdefg"n = 2

  1. 反转字符串的前 n 个字符 在这个步骤中,我们只反转字符串的前n个字符,也就是 "ab"。 初始字符串:"abcdefg" 执行此步骤后:"bacdefg" 使用reverse(s.begin(), s.begin() + n);进行反转。这里,s.begin()指向第一个字符,而s.begin() + n指向的是第n+1个字符的位置(也就是不包含在反转中的第一个字符的位置)。在C++中,reverse函数的结束迭代器是不包括在操作中的,所以这个反转确实只涉及到前n个字符。
  2. 反转字符串的剩余字符 现在,我们要反转从n位置开始的剩余字符,也就是 "cdefg"。 当前字符串:"bacdefg" 执行此步骤后:"bagfedc" 使用reverse(s.begin() + n, s.end());进行反转。这里,s.begin() + n指向第n+1个字符,而s.end()指向的是字符串之后的位置,所以这次反转涉及到从n位置开始的所有字符。
  3. 反转整个字符串 最后,我们反转整个字符串。 当前字符串:"bagfedc" 执行此步骤后:"cdefgab" 使用reverse(s.begin(), s.end());反转整个字符串。

题解

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        // 反转字符串的前 n 个字符
        reverse(s.begin(),s.begin()+n);
        // 反转字符串的剩余字符
        reverse(s.begin()+n,s.end());
        // 反转整个字符串
        reverse(s.begin(),s.end());
        return s;
    }
};