代码随想录算法训练营day08 | 344.反转字符串 541. 反转字符串II 05.替换空格 151.翻转字符串里的单词 58-II.左旋转字符串

108 阅读5分钟

344.反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

344. 反转字符串 - 力扣(Leetcode)

思路

使用双指针法,分别从数组的两端进行遍历,依次交换两个指针所指向的数组元素的值。

代码

class Solution {
    public void reverseString(char[] s) {
        int start=0,end=s.length-1;
        char tmp;

        while(start<end){
            tmp=s[start];
            s[start]=s[end];
            s[end]=tmp;

            start++;
            end--;
        }
    }
}

541. 反转字符串II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

541. 反转字符串 II - 力扣(Leetcode)

思路

先判断k与字符串s的关系,

  • 如果k > s.length(),则字符串的全部字符都要反转。
  • 否则,遍历字符串,以end标记当前元素的位置,字符串未遍历部分用_s来指代,_s为数组中下标为[end, s.length()-1]的元素。
  • s的每k个字符为单位进行判断,
    • end + k - 1 < s.length()时,_s部分字符串数量大于等于k
      1. 将下标为[end, end + k - 1]的数组进行反转
      2. end置为 end + k - 1
      3. 判断_s是否长度大于k,即end + k - 1 < s.length()是否成立
        • 若成立,则下标为[end, end + k - 1]的数组元素不反转,并将end置为 end + k - 1
        • 若不成立,则下标为[end, s.length() - 1]的数组元素不反转,字符串遍历结束
    • 否则,将_s全部反转。

Tips

  • end初始化为0,意味着指针当前指向的是待填入的元素,
    • end开始的k个字符的序列的下标为:[end,end+k-1]

代码

class Solution {
    public String reverseStr(String s, int k) {
        char[] res=new char[s.length()];

        if(s.length()<=k){
            for(int i=0;i<s.length();i++){
                res[i]=s.charAt(s.length()-i-1);
            }
            return new String(res);
        }

        int end=0;

        while(end+k-1<s.length()){
            for(int i=0;i<k;i++){
                res[end+i]=s.charAt(end+k-1-i);
            }

            end+=k;

            if(end+k-1<s.length()){
                for(int i=0;i<k;i++){
                    res[end]=s.charAt(end);
                    end++;
                }
            }else{
                while(end<s.length()){
                    res[end]=s.charAt(end);
                    end++;
                }
                return new String(res);
            }
        }

        // 全部翻转
        for(int i=end,j=s.length()-1;end<=j;i++,j--){
            res[i]=s.charAt(j);
        }

        return new String(res);
        
    }
}

05.替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

剑指 Offer 05. 替换空格 - 力扣(Leetcode)

思路

  • 方法1:暴力方法,遍历给定字符串,如果当前字符为空格,则在结果字符串中追加"%20",否则,将当前字符追加到目标字符串中。
  • 方法2:先遍历字符串,计算出空格的个数spaceNum,从而计算出目标字符串的长度为2 * spaceNum + s.length()。遍历给定字符串,如果当前字符为空格,则在结果字符串中追加"%20",否则,将当前字符追加到目标字符串中。
  • 两种方法的区别:方法1利用了 java 的可变字符串StringBuilder,因此不需要计算目标字符串的长度,StringBuilder的开销需要一定的空间,方法1是以空间换取时间;而方法2通过计算目标字符串的长度,从而可以使用字符串数组,用时间换取空间。

代码

第一种方法:使用StringBuilder保存结果

class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb=new StringBuilder();

        for(int i=0;i<s.length();i++){
            if(s.charAt(i)==' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }

        return sb.toString();
    }
}

第二种方法

class Solution {
    public String replaceSpace(String s) {
        int spaceNum=0;

        //空格的数量
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)==' '){
                spaceNum++;
            }
        }

        int len=2*spaceNum+s.length();
        char[] res=new char[len];

        for(int i=s.length()-1,j=len-1;i>=0;i--){
            if(s.charAt(i)==' '){
                res[j--]='0';
                res[j--]='2';
                res[j--]='%';
            }else{
                res[j--]=s.charAt(i);
            }
        }

        return String.valueOf(res);
    }
}

151.翻转字符串里的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意: 输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

151. 反转字符串中的单词 - 力扣(Leetcode)

思路

有两种思路

思路1
将每一个单词作为一个整体,题目就简化成将每一个整体按间隔一个空格的格式逆序输出。正序输入、逆序输出,即后入先出,是的特点。
遍历s,使用栈存储每一个单词。 遍历结束后,每出栈一个单词,拼接一个空格,并追加到目标字符串中。 出栈结束后,删除目标字符串中多增加的空格,返回结果。

思路2

  1. 对给定字符串s进行处理,去除字符串的前导空格、尾随空格或者单词间的多个空格,每个单词之间只以一个空格间隔,得到字符串_s
  2. _s整体进行反转(使用双指针法)。
  3. 遍历_s,对字符串中的每一个单词单独进行反转。(即局部使用双指针法反转字符串)
  4. 所有单词反转后,得到目标字符串。

反转字符串中的单词.jpg

代码

使用的方式,代码如下:

class Solution {
    public String reverseWords(String s) {
        Deque<String> stack=new LinkedList<>();

        int index=0;
        int start=0;

        boolean needToProcess=false;

        lable:while(index<s.length()){
            //去除前置空格
            while(s.charAt(index)==' '){
                index++;
                if(index==s.length()){
                    break lable;
                }
            }
            start=index;

            while(s.charAt(index)!=' '){
                index++;
                if(index==s.length()){
                    needToProcess=true;
                    break lable;
                }
            }

            stack.push(s.subSequence(start,index).toString());

        }

        // 处理最后一个字符串
        if(needToProcess){
            stack.push(s.subSequence(start,s.length()).toString());
        }

        StringBuilder sb=new StringBuilder();

        // 整理结果
        while(!stack.isEmpty()){
            sb.append(stack.pop()+" ");
        }

        sb.deleteCharAt(sb.length()-1);

        return sb.toString();


    }
}

使用思路2,代码如下:

class Solution {
    public String reverseWords(String s) {

        //去除空格
        StringBuilder sb=removerExtraSpace(s);

        //颠倒字符串
        int i=0,j=sb.length()-1;
        char tmp=0;
        while(i<j){
            tmp=sb.charAt(i);
            sb.setCharAt(i,sb.charAt(j));
            sb.setCharAt(j,tmp);

            i++;
            j--;
        }

        //颠倒word
        int start=0,end=0;
        while(end<sb.length()){
            while(end<sb.length()&&sb.charAt(end)!=' '){
                end++;
            }

            //颠倒word
            i=start;
            j=end-1;

            while(i<j){
                tmp=sb.charAt(i);
                sb.setCharAt(i,sb.charAt(j));
                sb.setCharAt(j,tmp);

                i++;
                j--;
            }

            end++;
            start=end;
        }

        return sb.toString();
    }

    private StringBuilder removerExtraSpace(String s){
        StringBuilder sb=new StringBuilder();

        for(int i=0,j=0;i<s.length();i++){
            if(s.charAt(i)!=' '){
                sb.append(s.charAt(i));
            }else if(!sb.isEmpty()&&i>0&&s.charAt(i-1)!=' '){
                sb.append(' ');
            }
        }

        for(int i=sb.length()-1;i>=0;i--){
            if(sb.charAt(i)==' '){
                sb.deleteCharAt(i);
            }else{
                break;
            }
        }

        return sb;
    }
}

58-II.左旋转字符串

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

剑指 Offer 58 - II. 左旋转字符串 - 力扣(Leetcode)

思路

  1. 将前n个字符反转。
  2. 将后s.length() - n个字符反转。
  3. 整体反转字符串,得到结果。
    对字符串进行整体反转,字符串前面的若干个字符自然就转移到字符串的尾部了,但此时,无论是之前字符串中的前n个字符还是后s.length() - n个字符,都是逆序的,将这两部分分别反转,即可获得目标字符串。

Tips

  1. 思路的 第3步 可以放在第1、2步之前,即可以先整体反转再局部反转,也可以先局部返回再整体反转。
  2. 局部反转时,可以使用双指针法,也可以不使用双指针法。
  3. 局部反转时不使用双指针法的方法,是将子字符串逆序输出到目标字符串的指定范围内。这样做是因为题目给定的字符串s是在程序运行期间不发生改变。

代码

class Solution {
    public String reverseLeftWords(String s, int n) {

        char[] str=s.toCharArray();

        // 反转s的前n个元素
        for(int i=n-1,j=0;i>=0;i--,j++){
            str[j]=s.charAt(i);
        }

        // 反转S的后s.length()-n个元素
        for(int i=s.length()-1,j=n;i>=n;i--,j++){
            str[j]=s.charAt(i);
        }

        // 整体反转字符串
        int start=0,end=s.length()-1;
        char tmp;
        while(start<end){
            tmp=str[start];
            str[start]=str[end];
            str[end]=tmp;
            start++;
            end--;
        }

        return new String(str);

    }
}

统一使用双指针进行字符串反转,代码如下:

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] _s=s.toCharArray();

        reverseRange(_s,0,s.length()-1);

        reverseRange(_s,0,s.length()-n-1);

        reverseRange(_s,s.length()-n,s.length()-1);

        return new String(_s);
    }

    private void reverseRange(char[] s,int start,int end){
        int i=start,j=end;
        char tmp=0;
        while(i<j){
            tmp=s[i];
            s[i]=s[j];
            s[j]=tmp;

            i++;
            j--;
        }
    }
}