代码随想录算法训练营第八天| 344.反转字符串 ● 541. 反转字符串II ●卡码网:54.替换数字 ● 151.翻转字符串里的单词 ● 卡码网:55.右旋转字符串

String字符串的一些基本知识

首先我们要知道的是Java中的String是不可变的 我们做的增删改等操作都是会再新建一个对象进行操作 因此Java中无法做到在原先的字符串中原地操作 为了避免创建很多对象我们可以采取转换成char数组或者使用StringBuilder

一些常用API

详见W3School 主要需要掌握charAt、indexOf、length、split等

反转字符串

题目链接

思路

可以类比数组的反转 很容易想到用双指针去完成

344.反转字符串

代码

class Solution {
    public void reverseString(char[] s) {
        int l = 0;
        int r = s.length - 1;
        while (l < r) {
            s[l] ^= s[r];  //构造 a ^ b 的结果,并放在 a 中
            s[r] ^= s[l];  //将 a ^ b 这一结果再 ^ b ,存入b中,此时 b = a, a = a ^ b
            s[l] ^= s[r];  //a ^ b 的结果再 ^ a ,存入 a 中,此时 b = a, a = b 完成交换
            l++;
            r--;
        }
    }
}

// 第二种方法用temp来交换数值更多人容易理解些
class Solution {
    public void reverseString(char[] s) {
        int l = 0;
        int r = s.length - 1;
        while(l < r){
            char temp = s[l];
            s[l] = s[r];
            s[r] = temp;
            l++;
            r--;
        }
    }
}


这里讲解一下异或是如何完成交换操作的:

s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
  • 这段代码是用来交换数组s中第i个元素和第j个元素的值的。它使用了异或运算符(^),它的作用是对两个二进制数的每一位进行逻辑异或,即相同为0,不同为1。例如,5 ^ 3 = 6,因为5的二进制是101,3的二进制是011,异或后得到110,即6。

这段代码的原理是利用了异或运算的以下性质:

1、任何数和自己异或都等于0,即x ^ x = 0

2、任何数和0异或都等于自己,即x ^ 0 = x

3、异或运算满足交换律和结合律,即x ^ y = y ^ x,(x ^ y) ^ z = x ^ (y ^ z)

所以,当执行第一句s[i] ^= s[j]时,相当于把s[i]和s[j]的值异或后赋给s[i]。此时s[i]变成了原来s[i]和s[j]的值的异或结果。

当执行第二句s[j] ^= s[i]时,相当于把新的s[i]和原来的s[j]再次异或后赋给s[j]。由于异或运算满足交换律和结合律,所以这相当于把原来的s[i]和原来的s[j]两次异或后赋给了s[j]。根据性质1和2,我们知道这就相当于把原来的s[i]赋给了新的s[j]。此时实现了一半交换。

当执行第三句s[i] ^= s[j];时,相当于把新的sj和新的si再次异或后赋给新的 s[i].由于同样满足交换律和结合律,所以这相当于把原来两者三次异或后赋给了新 的 s [i].根据性质1 和2 ,我们知道这就相当于把原来 的 s [j ] 赋 给 了 新 的 s [i ].此时实现了完全交换。

反转字符串II

题目链接

思路

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

如果剩余字符少于 k 个,则将剩余字符全部反转。

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

这道题我们需要记住**当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。** 比如我们int i = 0然后让i一次移动2k个单位 这样我们的i每次移动到要处理段的起点 然后再判断剩余字符是否满足大于k个

代码

class Solution {
        public String reverseStr(String s, int k) {
            //StringBuilder stringBuilder = new StringBuilder(s);
            char[] chars = s.toCharArray(); //String转为数组
            for (int i = 0; i < chars.length; i += 2 * k) {
                if (i + k > chars.length) { //不足k个 全部反转
                    reverse(chars, i, chars.length - 1);
                } else {
                    reverse(chars, i, i + k - 1);
                }

            }
            return new String(chars);

        }

        private void reverse(char[] chars, int start, int end) {
            while (start < end) {
								//同样用异或的方法
                chars[start] ^= chars[end];
                chars[end] ^= chars[start];
                chars[start] ^= chars[end];
                start++;
                end--;
            }

        }
    }

替换数字

题目链接

思路

这道题Java不能像C++一样直接操作字符串 但是其中的思想是值得学习的

题目要求遇见数字就替换成number 依然是采用双指针的想法 同时我们要注意:从后向前填充 避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。

img

代码

这里提供java实现的代码 比较容易 使用StringBuilder然后append即可

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s = in.nextLine();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (Character.isDigit(s.charAt(i))) { //内置函数判断是否是数字
                sb.append("number");
            }else sb.append(s.charAt(i));
        }
        System.out.println(sb);
    }
}

翻转字符串里的单词

题目链接

思路

这道题可谓是很难了 综合了多种操作 去除空格的同时还要完成反转的操作

首先我们可以先翻转整个字符串 然后再去反转单个单词 这样就可以完成我们想要的效果

首先讲一下如何去除空格 这其实很类似于数组的移除元素操作 只不过这里的元素替换成了空格

既然类似于数组的移除元素操作 我们还是要用双指针去完成操作:

fast和slow指向开头 然后进行遍历 还是fast指向要添加的元素 slow指向要添加的下标 但是这里我们还需要注意单词之间还要加空格 那怎么实现呢? fast 遇到空格或遍历到字符串末尾,就证明遍历完一个单词了 那在这个单词之后我们就要加上一个空格

代码

class Solution {
    //用 char[] 来实现 String 的 removeExtraSpaces,reverse 操作
    public String reverseWords(String s) {
        char[] chars = s.toCharArray();
        //1.去除首尾以及中间多余空格
        chars = removeExtraSpaces(chars);
        //2.整个字符串反转
        reverse(chars, 0, chars.length - 1);
        //3.单词反转
        reverseEachWord(chars);
        return new String(chars);
    }

    //1.用 快慢指针 去除首尾以及中间多余空格,可参考数组元素移除的题解
    public char[] removeExtraSpaces(char[] chars) {
        int slow = 0;
        for (int fast = 0; fast < chars.length; fast++) {
            //先用 fast 移除所有空格
            if (chars[fast] != ' ') {
                //在用 slow 加空格。 除第一个单词外,单词末尾要加空格
                if (slow != 0)
                    chars[slow++] = ' ';
                //fast 遇到空格或遍历到字符串末尾,就证明遍历完一个单词了
                while (fast < chars.length && chars[fast] != ' ')
                    chars[slow++] = chars[fast++];
            }
        }
        //相当于 c++ 里的 resize()
        char[] newChars = new char[slow];
        System.arraycopy(chars, 0, newChars, 0, slow); 
        return newChars;
    }

    //双指针实现指定范围内字符串反转,可参考字符串反转题解
    public void reverse(char[] chars, int left, int right) {
        if (right >= chars.length) {
            System.out.println("set a wrong right");
            return;
        }
        while (left < right) {
            chars[left] ^= chars[right];
            chars[right] ^= chars[left];
            chars[left] ^= chars[right];
            left++;
            right--;
        }
    }

    //3.单词反转
    private void reverseEachWord(char[] chars){
        int fast;
        int slow=0;
        for( fast =0;fast<=chars.length;fast++){
            if(fast == chars.length || chars[fast] == ' '){
                reverse(chars,slow,fast-1);
                slow=fast+1;
            }
        }
    }
}

右旋转字符串

题目链接

思路

这道题依然可以用先全部反转再局部反转的策略:

本题中,我们需要将字符串右移n位,字符串相当于分成了两个部分,如果n为2,符串相当于分成了两个部分,如图: (length为字符串长度)

img

右移n位, 就是将第二段放在前面,第一段放在后面,先不考虑里面字符的顺序,是不是整体倒叙不就行了。如图:

img

此时第一段和第二段的顺序是我们想要的,但里面的字符位置被我们倒叙,那么此时我们在把 第一段和第二段里面的字符再倒叙一把,这样字符顺序不就正确了。 如果:

img

其实,思路就是 通过 整体倒叙,把两段子串顺序颠倒,两个段子串里的的字符在倒叙一把,负负得正,这样就不影响子串里面字符的顺序了。

代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = Integer.parseInt(in.nextLine());
        String s = in.nextLine();

        int len = s.length();  //获取字符串长度
        char[] chars = s.toCharArray();
        reverseString(chars, 0, len - 1);  //反转整个字符串
        reverseString(chars, 0, n - 1);  //反转前一段字符串,此时的字符串首尾尾是0,n - 1
        reverseString(chars, n, len - 1);  //反转后一段字符串,此时的字符串首尾尾是n,len - 1
        
        System.out.println(chars);

    }

    public static void reverseString(char[] ch, int start, int end) {
        //异或法反转字符串,参照题目 344.反转字符串的解释
        while (start < end) {
            ch[start] ^= ch[end];
            ch[end] ^= ch[start];
            ch[start] ^= ch[end];
            start++;
            end--;
        }
    }
}

还有更容易的解法:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) { // 添加了String[] args
        Scanner in = new Scanner(System.in);
        int index = Integer.parseInt(in.nextLine());
        String s = in.nextLine();
        // 确保index在合理范围内
        if (index >= 0 && index <= s.length()) {
            System.out.println(s.substring(s.length() - index) + s.substring(0, s.length() - index)); // 将subString改为substring
        } else {
            System.out.println("Index out of bounds");
        }
    }
}

  • 63
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值