代码随想录 Day8 & Day 9

344. 反转字符串

这道题目比较简单,只说一下思路,采用双指针法。对于字符串,定义两个指针,一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。

541. 反转字符串II

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

本题用的基础逻辑还是和344. 反转字符串一样,重点是要找每2 * k 区间的起点以及双指针判断末尾指针的位置。如果末尾不足k个,end指针就指向字符串的最后一位;否则就指向第k位。

class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for(int i = 0; i < ch.length; i += 2 * k){
            int start = i;
            int end = start + k - 1 < ch.length - 1 ? start + k - 1 : ch.length - 1;
            while(start < end){
                char temp = ch[start];
                ch[start] = ch[end];
                ch[end] = temp;
                start++;
                end--;
            }
        }
    return new String(ch);
    }
}

Java使用技巧:

  • 字符串变为数组 : s. toCharArray()
  • 字符数组变为字符串:new String(ch)

剑指Offer 05.替换空格

方法一:创建一个新的StringBuilder对象,把字符串放进去。StringBuilder:一个可变的操作字符串的容器。这种方法比较简单,完整代码如下:

class Solution {
    public String replaceSpace(String s) {
        if(s == null){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
        //if (" ".equals(String.valueOf(s.charAt(i)))){}
            if(s.charAt(i) == ' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
    return sb.toString();
    }
}

方法二:采用双指针法。双指针法稍微麻烦一些,首先需要扩充容量,然后从后向前替换空格。right 指向新长度的末尾,left 指向旧长度的末尾。

class Solution {
    public String replaceSpace(String s) {
        if(s == null || s.length() == 0){
            return s;
        }

        //扩充容量到每个空格替换成"%20"之后的大小。
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i < s.length(); i++){
            if(s.charAt(i) == ' '){
                sb.append("  ");
            }
        }

        int left = s.length() - 1;
        s += sb.toString();
        int right = s.length() - 1;
        char[] ch = s.toCharArray();
        
        while(left >= 0){
            if(ch[left] != ' '){
                ch[right] = ch[left];
            }else{
                ch[right] = '0';
                ch[right - 1] = '2';
                ch[right - 2] = '%';
                right -= 2;
            }
            left--;
            right--;
        }
        return new String(ch);       
    }
}

151 反转字符串中的单词

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

方法一:
第一种想法就是自然而然的想到,通过创建一个新的char[ ]数组,从字符串末尾开始,通过双指针 i 和 right 来判断每个单词的收尾位置,然后放入新创建的数组中。其实就相当于创建了一个新的字符数组进行填充。

class Solution {
    public String reverseWords(String s) {

        char[] arr = s.toCharArray();
        char[] new_arr = new char[arr.length + 1];
        //定义新数组的下标
        int newArrPos = 0;
        //从原来字符串的末尾开始遍历
        int i = arr.length - 1;
        while(i >= 0){
            while(i >= 0 && arr[i] == ' '){
                i--;
            }
            int right = i;

            while(i >= 0 && arr[i] != ' '){
                i--;
            }

            //i+1 到 right 之间的就是每个单词
            for(int j = i + 1; j <= right; j++){
                new_arr[newArrPos++] = arr[j];
                if(j == right){
                    new_arr[newArrPos] = ' ';
                    newArrPos++;
                }
            }
        }
        //注意这个时候,如果直接返回new String(new_arr)最后一个单词仍然带有空格
        return new String(new_arr,0,newArrPos - 1);

    }
}

看了代码随想录的方法后,考虑不额外创建新的数组,采用整体反转+局部反转的思想。主要的思想步骤分为三步:

  1. 先删除多余的空格和首尾空格;
  2. 将整个字符串反转;
  3. 将每个单词反转。

方法二:
考虑使用字符串的char数组,按三步走。需要注意边界条件要处理,不然会造成数组溢出!!
完整代码如下:

class Solution {
    public String reverseWords(String s) {
        char[] s1 = remove(s);
        //System.out.println(s);
        char[] s2 = reverseAll(s1, 0, s1.length - 1);
        //System.out.println(s2);
        char[] s3 = reverseOne(s2);
        //System.out.println(s3);
        return new String(s3);

    }

        private static char[] remove(String s){
        char[] arr = s.toCharArray();
        int slow = 0;
        for(int quick = 0; quick < arr.length; quick++){
            if(arr[quick] != ' '){
                if(slow != 0){
                    arr[slow++] = ' ';
                }
                //这里缺少quick < arr.length就会报错,ArrayIndexOutOfBoundsException
                while(quick < arr.length && arr[quick] != ' '){
                    arr[slow++] = arr[quick++];
                }
            }
        }
        //相当于C++的resize
        char[] new_arr = new char[slow];
        System.arraycopy(arr, 0, new_arr, 0, slow);
        return  new_arr;

    }

    private static char[] reverseAll(char[] ch, int start, int end){

        while(start < end){
            char temp = ch[start];
            ch[start] = ch[end];
            ch[end] = temp;
            start++;
            end--;
        }
        return ch;
    }

    private static char[] reverseOne(char[] ch){
        int start = 0;
        for(int end = 1;end < ch.length; end++){
        //每次边界条件很容易忘记写造成数组溢出错误!
            while(end < ch.length && ch[end] != ' '){
                end++;
            }
            reverseAll(ch,start,end - 1);
            start = end + 1;
        }
        return ch;
    }

}

方法三:仍然按照三步走,使用StringBuilder容器。Java的StringBuilder真的很好用!

class Solution {
    public String reverseWords(String s) {
        StringBuilder s1 = remove(s);
        StringBuilder s2 = reverseAll(s1, 0, s1.length() - 1);
        StringBuilder sb = reverseOne(s2);
        return sb.toString();

    }

       private static StringBuilder remove(String s){
        StringBuilder sb = new StringBuilder();
        int start = 0;
        int end = s.length() - 1;
        while(s.charAt(start) == ' '){
            start++;
        }
        while(s.charAt(end) == ' '){
            end--;
        }

        for(int i = start; i <= end; i++){
            char c= s.charAt(i);
            //这里判断的地方注意理解 是用来删除多余空格的
            if ( !(c == ' ' && sb.charAt(sb.length() - 1) == ' ')){
                sb.append(c);
            }
        }

        return sb;

    }

    private static StringBuilder reverseAll(StringBuilder sb, int start, int end){

        while(start < end){
            char temp = sb.charAt(start);
            //sb.charAt(start)= sb.charAt(end);不可以直接这么写
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
        }

        return sb;
    }

    private static StringBuilder reverseOne(StringBuilder sb){
        int start = 0;
        for(int end = 1; end < sb.length(); end++){
            while(end < sb.length() && sb.charAt(end) != ' '){
                end++;
            }
            reverseAll(sb,start,end - 1);
            start = end + 1;
        }

        return sb;
    }
}

使用方法三的时候需要注意,//sb.charAt(start)= sb.charAt(end);在交换元素时,StringBuilder对象是不可以直接这么赋值的,需要使用setCharAt()方法

public void setCharAt(int pos, char c);
参数:
int pos –表示我们要设置给定字符的位置。
char c –表示我们要放置的新字符。

剑指Offer58-II 左旋转字符串

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

通过151 反转字符串中的单词的学习,可以考虑通过使用局部反转 + 整体反转来实现反转单词顺序的目的。也是分为三步走:

  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串

完整代码如下:

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] ch = s.toCharArray();
        reverse(ch, 0, n - 1);
        reverse(ch, n, ch.length - 1);
        char[] new_ch = reverse(ch, 0, ch.length - 1);
        return new String(new_ch);

    }

    private char[] reverse(char[] ch, int start, int end){
        while(start < end){
            char temp = ch[start];
            ch[start] = ch[end];
            ch[end] = temp;
            start++;
            end--;
        }
        return ch;
    }
}

总结

344.反转字符串,是最简单的处理,采用双指针法,后面的习题也都用到了本题的基础框架。

541.反转字符串II,这道题给反转加上了一些条件,注意:当需要固定规律一段一段去处理字符串的时候,可以在for循环的表达式上做做文章。

151.翻转字符串里的单词中,要对一句话里的单词顺序进行反转。首先需要对空格的格式进行处理,整体反转 + 局部反转的思路很巧妙。

剑指Offer58-II 左旋转字符串,与 151.翻转字符串里的单词 类似,先局部反转再 整体反转。

可以看出用Java做字符串的题目,要么是使用StringBuilder来解决,要么是将字符串转为为字符数组来处理。

28. 找出字符串中第一个匹配项的下标

KMP算法的典型应用
方法一:暴力解决

class Solution {
    public int strStr(String haystack, String needle) {
        //如果needle是空字符串,应该返回0
        if (needle.length() == 0) {
            return 0;
        }

        if (haystack.length() < needle.length()) {
            return -1;
        }

         for (int i = 0; i < haystack.length(); i++) {
            if(i + needle.length() > haystack.length()){
                return -1;
            }
            int index = i;
            int j = 0;
            while (j < needle.length()){
                if(haystack.charAt(index) == needle.charAt(j)){
                    if(j == needle.length() - 1){
                        return i;
                    }
                    index++;
                    j++;
                }else{
                    break;
                }
            }
        }
        return -1;
    }
}

方法二:KMP

class Solution {


    public int strStr(String haystack, String needle) {
        if(needle.length() == 0) return 0;
        int[] next = new int[needle.length()];
        getNext(next,needle);

        int j = 0;
        for(int i = 0; i < haystack.length();i++){
            while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
                j = next[j - 1];
            }
            if(haystack.charAt(i) == needle.charAt(j)){
                j++;
            }
            if(j == needle.length()){
                return i - needle.length() + 1;
            }
        }
        return -1;



    }

    private void getNext(int[] next, String s){
        int j = 0;
        next[0] = 0;
        for (int i = 1; i < s.length(); i++) {
            while(j > 0 && s.charAt(i) != s.charAt(j)){
                j = next[j-1];
            }
            if(s.charAt(i) == s.charAt(j)){
                j++;
            }
            next[i] = j;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值