代码随想录算法训练营第七天|反转字符、反转字符串II、替换数字 今天一整个大开心请看文章!!!

官方:对于今天这些题目一些同学直接用一个库函数 reverse,调一下直接完事了, 相信每一门编程语言都有这样的库函数。如果这么做题的话,这样大家不会清楚反转字符串的实现原理了。

但是也不是说库函数就不能用,是要分场景的。

如果在现场面试中,我们什么时候使用库函数,什么时候不要用库函数呢?

如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。

毕竟面试官一定不是考察你对库函数的熟悉程度, 如果使用python和java 的同学更需要注意这一点,因为python、java提供的库函数十分丰富。

如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。

建议大家平时在leetcode上练习算法的时候本着这样的原则去练习,这样才有助于我们对算法的理解。

一、反转字符串

1.题目描述

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 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"]

2.思路

1.常规思路(双指针)

利用一个temp实现反转,这个很常见,我一看题目也是用的这个方法

2.异或运算(不使用额外的临时变量,可以节省一点内存空间)

异或运算(^)有三个重要特性:

  1. 任何数和自身异或结果为 0(a ^ a = 0
  2. 任何数和 0 异或结果还是自身(a ^ 0 = a
  3. 异或运算满足交换律和结合律(a ^ b ^ c = a ^ (b ^ c)

假设当前lr指向的字符分别为ab

  1. 第一步:s[l] ^= s[r]

    • 等价于 s[l] = s[l] ^ s[r]
    • 此时s[l]的值变为 a ^ bs[r]仍为b
  2. 第二步:s[r] ^= s[l]

    • 等价于 s[r] = s[r] ^ s[l]
    • 代入值后:s[r] = b ^ (a ^ b) = a(根据异或特性)
    • 此时s[r]的值变为as[l]仍为a ^ b
  3. 第三步:s[l] ^= s[r]

    • 等价于 s[l] = s[l] ^ s[r]
    • 代入值后:s[l] = (a ^ b) ^ a = b(根据异或特性)
    • 此时s[l]的值变为bs[r]a,完成交换
  4. 最后移动指针:l++(左指针右移),r--(右指针左移)

3.代码 

1.常规思路
class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length-1;

        while (left<right){
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }

    }
}
2.异或运算
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--;
        }
    }
}

二、反转字符串II

1.题目描述

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

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

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

示例:

输入: s = "abcdefg", k = 2
输出: "bacdfeg"

2.思路

1.最容易理解思路
  1. 分组规则:以2k为步长遍历字符串(i += 2 * k),把字符串分成若干段
  2. 处理每组:对每段的前k个字符进行反转
  3. 边界兼容:当剩余字符不足k2k时,按规则处理

 我再看到之后还是想用for循环和temp进行编写,每 2k 个字符为一组,反转每组前 k 个字符"的规则处理字符串,其中我发现一个官方很好的方法int end = Math.min(最大索引, 理想情况下要反转的终点索引);这行代码堪称点睛之笔,它的巧妙之处在于用一行行代码同时处理了所有边界情况,具体代码可以看3.代码中的完整代码!我觉得真的很巧妙,我自己写的时候为了这个多余的几个数,写了if很多条语句,这个就是一行就完事了!!!!

2.定义反转函数

这个直接看完整代码就可以啦

3.代码

1.最容易理解的思路
class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for (int i = 0; i < s.length(); i+=2*k) {
            int start = i;
            int end = Math.min(ch.length - 1,start + k - 1);//巧妙之处
            while(start < end){
                char temp = ch[start];
                ch[start] = ch[end];
                ch[end] = temp;
                start++;
                end--;
            }
        }
        return new String(ch);
    }
}

代码: int end = Math.min(ch.length - 1,start + k - 1);//巧妙之处

start 是当前区间的起始索引(每次循环从 i 开始,步长为 2k

start + k - 1 是「理想情况下」要反转的终点索引(即从 start 开始数 k 个字符的位置)

ch.length - 1 是字符数组的最后一个索引(避免越界)

Math.min(a, b) 取两者中的较小值作为实际终点

      我写到这里好兴奋!!!!!就是一下知道了这个一行代码 的思路就很巧妙哈哈哈哈!!!

就是不用写很多代码去完成不足2k的部分,真巧妙啊

2.定义反转函数

class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        // 1. 每隔 2k 个字符的前 k 个字符进行反转
        for (int i = 0; i< ch.length; i += 2 * k) {
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= ch.length) {
                reverse(ch, i, i + k -1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转
            reverse(ch, i, ch.length - 1);
        }
        return  new String(ch);

    }
    // 定义翻转函数
    public void reverse(char[] ch, int i, int j) {
    for (; i < j; i++, j--) {
        char temp  = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }

    }
}

三、替换数字

1.题目描述

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。

例如,对于输入字符串 "a1b2c3",函数应该将其转换为 "anumberbnumbercnumber"。

对于输入字符串 "a5b",函数应该将其转换为 "anumberb"

输入:一个字符串 s,s 仅包含小写字母和数字字符。

输出:打印一个新的字符串,其中每个数字字符都被替换为了number

样例输入:a1b2c3

样例输出:anumberbnumbercnumber

数据范围:1 <= s.length < 10000。

2.思路

我自己的这个思路还不错我感觉,不用一个字符一个字符添加number比如

 newS[i] = 'r';
                newS[i - 1] = 'e';
                newS[i - 2] = 'b';
                newS[i - 3] = 'm';
                newS[i - 4] = 'u';
                newS[i - 5] = 'n';

我感觉有点繁琐,接下来是我的思路 但是第一版写完后我发现有报错,成功改正:(请看VCR)

初始思路(第一版错误代码)

  1. 核心目标:将输入字符串中的每个数字字符替换为 "number",其他字符保持不变

  2. 实现计划

    • 读取用户输入的字符串
    • 创建一个足够大的字符数组(长度为原字符串的 6 倍,因为 "number" 是 6 个字符)
    • 遍历输入字符串的每个字符
    • 若为数字,则将 "number" 逐个字符存入新数组;否则直接存入原字符
    • 输出处理后的结果
  3. 遇到的报错

    • 使用 for (char ch : input) 遍历字符串时出现错误
    • 原因:Java 中字符串(String)不能直接用增强 for 循环遍历,增强 for 循环需要遍历的是数组或实现了 Iterable 接口的集合类

改进思路(第二版最终代码)

  1. 问题定位:明确错误是因为字符串不能直接用于增强 for 循环遍历
  2. 解决方案
    • 新增一步:将字符串转换为字符数组 char[] input = in.toCharArray()
    • 用转换后的字符数组进行增强 for 循环遍历 for (char ch : input)

3.代码

自己的实现代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String in = sc.next();
        char[] input = in.toCharArray();
        char[] input1 = new char[in.length() * 6]; // 预留足够空间
        int index = 0; // 用于追踪input1数组的当前位置

        for (char ch : input) {
            if ('0' <= ch && ch <= '9') {
                // 如果是数字字符,转换为"number"并逐个存入数组
                String number = "number";
                for (int i = 0; i < number.length(); i++) {
                    input1[index++] = number.charAt(i);
                }
            } else {
                // 非数字字符直接存入数组
                input1[index++] = ch;
            }
        }
        System.out.println(new String(input1, 0, index));
    }
}

开心滴锣鼓!!!!

官方代码

解法一:

import java.util.Scanner;

public class Main {
    
    public static String replaceNumber(String s) {
        int count = 0; // 统计数字的个数
        int sOldSize = s.length();
        for (int i = 0; i < s.length(); i++) {
            if(Character.isDigit(s.charAt(i))){
                count++;
            }
        }
        // 扩充字符串s的大小,也就是每个空格替换成"number"之后的大小
        char[] newS = new char[s.length() + count * 5];
        int sNewSize = newS.length;
        // 将旧字符串的内容填入新数组
        System.arraycopy(s.toCharArray(), 0, newS, 0, sOldSize);
        // 从后先前将空格替换为"number"
        for (int i = sNewSize - 1, j = sOldSize - 1; j < i; j--, i--) {
            if (!Character.isDigit(newS[j])) {
                newS[i] = newS[j];
            } else {
                newS[i] = 'r';
                newS[i - 1] = 'e';
                newS[i - 2] = 'b';
                newS[i - 3] = 'm';
                newS[i - 4] = 'u';
                newS[i - 5] = 'n';
                i -= 5;
            }
        }
        return new String(newS);
    };
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.next();
        System.out.println(replaceNumber(s));
        scanner.close();
    }
}

解法二

// 为了还原题目本意,先把原数组复制到扩展长度后的新数组,然后不再使用原数组、原地对新数组进行操作。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int len = s.length();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) >= 0 && s.charAt(i) <= '9') {
                len += 5;
            }
        }
        
        char[] ret = new char[len];
        for (int i = 0; i < s.length(); i++) {
            ret[i] = s.charAt(i);
        }
        for (int i = s.length() - 1, j = len - 1; i >= 0; i--) {
            if ('0' <= ret[i] && ret[i] <= '9') {
                ret[j--] = 'r';
                ret[j--] = 'e';
                ret[j--] = 'b';
                ret[j--] = 'm';
                ret[j--] = 'u';
                ret[j--] = 'n';
            } else {
                ret[j--] = ret[i];
            }
        }
        System.out.println(ret);
    }
}

四、个人心得(真实)

今天一整个大开心,大开心!!!先学习反转字符串II时候,发现官方的的代码一行可以代替我绞尽脑汁想出来很多if语句处理不足2k的数字,很开心了,然后最后替换是数字,自己的思路就少写了很多条代码,也学习了官方代码,则么说我也不知道时间复杂度怎么个比较,反正很开心,动力十足!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值