算法学习第七天| 力扣344.反转字符串,541. 反转字符串II,剑指Offer 05.替换空格,151.翻转字符串里的单词,剑指Offer58-II.左旋转字符串


今天学习了有关字符串的知识,也做了有关字符串的题目,在做字符串相关题目的时候,我们要想到双指针进行操作,往往涉及交换,填充的题目,我们都会用到双指针。而且对于填充的题目,我们选择从后往前处理也会减轻解题的难度。还有,有关字符串反转的题目,优势我们也要考虑通过多次整体反转来解题,例如:剑指Offer58-II.左旋转字符串。

相关题目:
● 344.反转字符串:https://leetcode.cn/problems/reverse-string/
● 541. 反转字符串II:https://leetcode.cn/problems/reverse-string-ii/
● 剑指Offer 05.替换空格:https://leetcode.cn/problems/ti-huan-kong-ge-lcof/
● 151.翻转字符串里的单词:https://leetcode.cn/problems/reverse-words-in-a-string/
● 剑指Offer58-II.左旋转字符串:https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/

关于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”]

该题的思路:
● 有关字符串的题目,我们都要有意识的想到用双指针解决它
● 在这题反转字符串中,我们可以定义两个指针,首先先指向头尾两个位置
● 然后通过临时变量的帮助交换这两个元素,交换完后就进行指针的更新
● 直到left指针大于等于right指针,即循环条件是left<right
● 为什么不用等号呢,因为要是left==right,则说明只剩下最后一个元素,自然就不用交换了
在这里插入图片描述
代码实现

class Solution {
    public void reverseString(char[] s) {

        //定义双指针,初始化在头尾
        int left=0,right=s.length-1;
        //临时变量
        char temp;

        //循环交换知道left>=right
        while (left<right){
            temp=s[left];
            s[left]=s[right];
            s[right]=temp;
            left++;
            right--;
        }

    }
}

关于541. 反转字符串II

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

该题的思路:
● 该题和上一题类似,也是用双指针解决比较好,但这题要求每2k个字符就反转前K个字符,对范围的取值就比较需要思考。并且要求尾部剩余字符少于 k 个,则将剩余字符全部反转。那么该怎么比较好操作呢
● 我们可以以2K为一次循环进行操作,在每次循环中以2K所在下标为起点进行反转K个字符,但这时要注意对尾部元素个数的判断和操作。即对右指针right的取值进行判断
● 对right的取值需要好好思考:right = Math.min(chars.length - 1,i + k - 1);
在这里插入图片描述
代码实现:

class Solution {
    public String reverseStr(String s, int k) {

        char[] chars = s.toCharArray();
        //定义双指针,初始化在头尾
        int left, right;

        //每次i+2*k为一个范围,以i为起点,翻转k个元素
        for (int i = 0; i < chars.length; i += 2 * k) {
            left = i;
            //这个判断很重要,关系到最后一组交换,必须理解
            right = Math.min(chars.length - 1,i + k - 1);
            //临时变量
            char temp;
            //循环交换直到left>=right
            while (left < right) {
                temp = chars[left];
                chars[left] = chars[right];
                chars[right] = temp;
                left++;
                right--;
            }
        }
        return new String(chars);

    }
}

关于 剑指Offer 05.替换空格

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

该题的思路:
● 遇到数组填充的问题,一般都需要扩充数组,扩充完数组后一般选择从后面开始操作,即从后向前操作,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
● 第一种方式是new一个可操作的StringBuilder()新字符串,直接遍历字符串s,若遇到空格就进行替换。
● 第二种方式是先统计好空格的数量,再对字符串s进行扩充,注意,扩充的时候要仔细想需要扩充到多大,因为空格替换为%20是三个字符,而原本空格就有一个字符了,故只需要多加 空格数量*2 这么大的字符长度即可。
● 定义左指针在原字符串的尾部,右指针在新字符串的尾部,不断判断左指针是否为空格,若为空格,右指针进行填充%20操作,若不为空格,则填充为左指针所在元素的值
在这里插入图片描述
代码实现:第一种方式new一个可操作的StringBuilder()新字符串

class Solution {
    public String replaceSpace(String s) {
        if (s == null) {
            return null;
        }
        //选用 StringBuilder 单线程使用,比较快,选不选都行
        StringBuilder sb = new StringBuilder();
        //使用 sb 逐个复制 s ,碰到空格则替换,否则直接复制
        for (int i = 0; i < s.length(); i++) {
            //s.charAt(i) 为 char 类型,为了比较需要将其转为和 " " 相同的字符串类型
            //if (" ".equals(String.valueOf(s.charAt(i)))){}
            if (s.charAt(i) == ' ') {
                sb.append("%20");
            } else {
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}

方式二先统计好空格的数量

class Solution {
    public String replaceSpace(String s) {
        //检验非空 若为空则直接返回原数组
        if (s == null || s.length() == 0) {
            return s;
        }

        //统计空格数量并替换成两倍的空格,用str保存
        //为什么 %20 是三个字符却用两倍的空格,因为是增加两倍的空格,而不是替换,加上之前原本就有的刚好三个
        StringBuffer str = new StringBuffer();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                str.append("  ");
            }
        }
        //若果str长度为0,说明s无空格,直接返回
        if (str.length() == 0) {
            return s;
        }
        //定义双指针,分别在新旧字符串的最后一个元素
        int left = s.length() - 1;
        String newS = s + str;
        int right = newS.length() - 1;
        char[] chars = newS.toCharArray();

        while (left >= 0) {
            //遇到空格就进行替换,注意--的使用,在脑海中要模拟一遍
            if (chars[left] == ' ') {
                chars[right--] = '0';
                chars[right--] = '2';
                chars[right] = '%';
            } else {
                chars[right] = chars[left];
            }
            left--;
            right--;
        }
        return new String(chars);
    }
}

关于151.翻转字符串里的单词

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

该题的思路:
● 该题要求反转字符串中的单词,但是单词还是原来的顺序,并且还要取出多余的不必要的空格
● 则我们可以先对字符串进行多余空格删除的操作,保证字符串中没有多余的空格
● 然后再对整个字符串反转
● 最后再对每个单词进行反转
● 举个例子,源字符串为:"the sky is blue "
● 1-移除多余空格 : “the sky is blue”
● 2-字符串反转:“eulb si yks eht”
● 3-单词反转:“blue is sky the”
● 这样我们就完成了翻转字符串里的单词。
● 每一个步骤都是对应一个方法,各自编写方法即可
● 方式二也可以调用内置的 API 完成,先split分割,再reverse反转,最后join拼接。

代码实现

class Solution {
    public String reverseWords(String s) {
        //先处理多余的空格,前后头尾不能有空格,单词之间最多有一个空格
        StringBuffer stringBuffer = removeSpace(s);
        //然后反转整个字符串
        reverseString(stringBuffer, 0, stringBuffer.length() - 1);
        //最后反转各个单词
        reverseWord(stringBuffer);
        return stringBuffer.toString();
    }

    //处理多余的空格
    private StringBuffer removeSpace(String s) {

        //定义双指针,分别在头尾
        int left = 0, right = s.length() - 1;

        //去除最前面多余的空格
        while (s.charAt(left) == ' ') {
            left++;
        }
        //去除最后面多余的空格
        while (s.charAt(right) == ' ') {
            right--;
        }

        //去除字符串中间多余的空格,单词间最多可以一个空格
        StringBuffer stringBuffer = new StringBuffer();
        //left一直走,right不动,直到left>right
        while (left <= right) {
            //一个一个取出来比较
            char c = s.charAt(left);
            /**
             * 妙!!
             * (1)若c==' ' && stringBuffer.charAt(stringBuffer.length() - 1) == ' ')
             *    则说明有连续的空格,不加第二个空格,不走里面的语句
             * (2)若c==' ' && stringBuffer.charAt(stringBuffer.length() - 1) != ' '
             *    则说明这是遇到的第一个空格,该添加上去
             * (3)若c!= ' ' && stringBuffer.charAt(stringBuffer.length() - 1) == ' '
             *    则说明这是一个新单词的开头字母
             * (4)若c!= ' ' || stringBuffer.charAt(stringBuffer.length() - 1) != ' '
             *    则说明这是一个单词的一个部分,应添加上去
             */
            if (c != ' ' || stringBuffer.charAt(stringBuffer.length() - 1) != ' ') {
                stringBuffer.append(c);
            }
            left++;
        }

        return stringBuffer;
    }

    //反转字符串 闭区间[left,right]
    private void reverseString(StringBuffer sb, int left, int right) {
        char temp;
        while (left < right) {
            temp = sb.charAt(left);
            sb.setCharAt(left, sb.charAt(right));
            sb.setCharAt(right, temp);
            left++;
            right--;
        }
    }

    //反转单词 闭区间[left,right]
    private void reverseWord(StringBuffer sb) {
        int left = 0;
        int right = 1;
        int n = sb.length();

        while (left < n) {
            //若不是空格,则往前走
            //条件right < n 一定要放在条件sb.charAt(right) != ' '的前面
            //否则,会遇到right==n而导致的错误,即栈溢出
            while (right < n && sb.charAt(right) != ' ') {
                right++;
            }
            //到这里,当前位置遇到了空格,则进行反转,由于是闭区间,故right-1
            reverseString(sb, left, right - 1);
            //更新left和right的位置
            left = right + 1;
            right = left + 1;
        }

    }
}

关于剑指Offer58-II.左旋转字符串

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

该题的思路:
对于该题,需要明确什么是左旋转字符串,理解了之后,方式有很多。
● 方式一:使用字符串切片的方式,即先取出n后面的字符串再拼接前n个字符
● 即s.substring(n, s.length()) + s.substring(0, n);
● 方式二,若不允许使用切片方式,则也可以列表遍历拼接,思想和方式一一样,只不过需要新建一个StringBuilder变量res进行操作
● 方式三,通过反转反转再反转的方式
● 即先反转前n个字符,再反转后面的字符,最后反转整个字符串,如下图
在这里插入图片描述
代码实现

class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuffer sb = new StringBuffer(s);

        //先反转前n个字符
        reverseString(sb,0,n-1);
        //在再反转后面的字符
        reverseString(sb,n,sb.length()-1);
        //最后反转整个字符串
        reverseString(sb,0,sb.length()-1);

        return sb.toString();
    }

    //反转闭区间[left,right]的顺序
    private void reverseString(StringBuffer sb, int left, int right) {
        char temp;
        while (left < right) {
            temp = sb.charAt(left);
            sb.setCharAt(left, sb.charAt(right));
            sb.setCharAt(right, temp);
            left++;
            right--;
        }
    }
}

第七天结束,第八天加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值