代码随想录算法训练营Day08|344.反转字符串| 541. 反转字符串II |替换数字

关于库函数的使用:
如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。
如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。

题目1:344.反转字符串

在这里插入图片描述
方法一:用temp

//用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--;
        }
    }
}

方法二:用异或性质

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--;
        }
    }
}

使用异或交换两个数

通过异或的性质可以实现两个变量交换值而不需要使用临时变量

假设有两个变量 a 和 b,希望交换它们的值,可以按照以下步骤进行:

步骤 1: a = a ^ b
将 a 和 b 进行异或运算,结果存回 a 中。此时,a 存储了 a ^ b 的值。

步骤 2: b = a ^ b
接下来,将更新后的 a(即 a ^ b)和 b 进行异或运算,结果存回 b 中。由于 a = a ^ b,所以 b = (a ^ b) ^ b。根据异或的性质 a ^ a = 0 和 a ^ 0 = a,我们可以得到:b=a ,此时 b 存储了原来 a 的值。

步骤 3: a = a ^ b
最后,将更新后的 b(此时 b 是原来的 a)与 a(当前 a 存储的是 a ^ b)进行异或运算,结果存回 a 中。由于 a = a ^ b,且 b = a,所以:在这里插入图片描述
根据异或的自反性 a ^ a = 0,所以 a = b,此时 a 存储了原来 b 的值。

到这里为止,我们通过三次异或运算完成了 a 和 b 的交换:

a 最终变成了原来 b 的值。
b 最终变成了原来 a 的值。

题目2:541. 反转字符串II

在这里插入图片描述
即:对字符串 s 中的每一个 2k 长度的子字符串,反转其前 k 个字符。如果剩下的字符不足 k 个,反转整个剩余部分。

// 用temp来交换数值
class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();//由于字符串是不可变的,为了能够在原地修改字符,需要将其转化为字符数组 ch。
        for(int i = 0;i < ch.length;i += 2 * k){
            int start = i;
            // 判断尾数够不够k个来取决end指针的位置
            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);
    }
}
for (int i = 0; i < ch.length; i += 2 * k) {
    int start = i;
    int end = Math.min(ch.length - 1, start + k - 1);
  • i 是当前块的起始位置,每次增加 2k,确保按照每 2k 个字符进行分段。
  • start 是当前块的起始位置。
  • end 是该块的结束位置(但是我们只反转前 k 个字符)。Math.min(ch.length - 1, start + k - 1) 是为了确保 end 不会越界,即处理剩余字符时不会越界。
while (start < end) {
    char temp = ch[start];
    ch[start] = ch[end];
    ch[end] = temp;

    start++;
    end--;
}

使用两个指针 start 和 end,从块的两端向中间反转字符。每次交换 ch[start] 和 ch[end],然后指针分别向中间移动。

return new String(ch);最后将字符数组转换回字符串并返回。

示例:

String s = "abcdefg";
int k = 2;
  1. 第一次循环,处理从 i = 0 到 i + 2k - 1 = 3 的字符:
    反转前 k = 2 个字符,即 “ab” 反转为 “ba”,剩下 “cdefg”。
  2. 第二次循环,处理从 i = 4 到 i + 2k - 1 = 7 的字符:
    反转前 k = 2 个字符,即 “cd” 反转为 “dc”,剩下 “efg”。
  3. 第三次循环,处理剩下的 “efg”,因为剩余字符少于 k,所以反转整个 “efg” 为 “gfe”。

最终输出:“bacdfeg”

时间复杂度分析
假设字符串长度为 n,我们每次处理 2k 长度的区块。
每个区块的反转操作需要 O(k) 时间,处理总共有 n / (2k) 个区块。
因此,总的时间复杂度为 O(n),即遍历字符串一次。

空间复杂度分析
由于我们只使用了常数级别的额外空间(除了字符数组之外),所以空间复杂度是 O(1)。

总结
这段代码通过分段反转字符,实现了题目要求的功能。每个 2k 长度的块中,反转前 k 个字符,剩余部分不动。它的时间复杂度为 O(n),空间复杂度为 O(1),是一个高效的解决方案。

题目3:替换数字

在这里插入图片描述
Java里的string不能修改,需要使用辅助空间。

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();
    }
}

解析:

public static String replaceNumber(String s) {
    int count = 0; // count:用来统计字符串 s 中数字字符的个数。
    int sOldSize = s.length();
    for (int i = 0; i < s.length(); i++) {
        if(Character.isDigit(s.charAt(i))){
            count++;
        }
    }

通过遍历字符串 s,每次遇到一个数字字符 (Character.isDigit(s.charAt(i))),就将 count 加 1。

    char[] newS = new char[s.length() + count * 5];
    int sNewSize = newS.length;

newS:用来存放替换后的字符串。初始时,它的大小为原字符串 s 的长度加上 count * 5。
count * 5 是因为每个数字将被替换为 “number”,而 “number” 是 6 个字符,因此需要多分配 5 个位置来存放额外的字符。
sNewSize 是 newS 数组的大小。

    System.arraycopy(s.toCharArray(), 0, newS, 0, sOldSize);

System.arraycopy 将原始字符串 s(转化为字符数组)复制到 newS 数组的前部分。
这一步的目的是将原字符串的内容保留下来,以便在后续处理中进行修改。

System.arraycopy 是 Java 中的一个系统级方法,用于快速地将一个数组的内容复制到另一个数组中。

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
  • src:源数组,即要从中复制数据的数组。
  • srcPos:源数组的起始位置,表示从源数组的哪个位置开始复制数据。
  • dest:目标数组,数据将被复制到该数组中。
  • destPos:目标数组的起始位置,表示目标数组从哪个位置开始接收数据。
  • length:要复制的元素数量。

从后向前替换数字字符:

    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;
        }
    }

  • i 和 j 分别是新数组和旧数组的指针,i 从新数组的尾部开始,j 从旧字符串的尾部开始。
  • 每次循环:
    如果 newS[j] 不是数字字符(!Character.isDigit(newS[j])),则直接将 newS[j] 复制到 newS[i]。
    如果 newS[j] 是数字字符,则将其替换为 “number” 的字符。具体来说:
    将 ‘r’ 放在 newS[i]。
    将 ‘e’ 放在 newS[i-1]。
    将 ‘b’ 放在 newS[i-2]。
    将 ‘m’ 放在 newS[i-3]。
    将 ‘u’ 放在 newS[i-4]。
    将 ‘n’ 放在 newS[i-5]。
  • 完成替换后,i 向前移动 5 位(i -= 5),跳过已经填充的 “number”。

main 方法:

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    String s = scanner.next();
    System.out.println(replaceNumber(s));
    scanner.close();
}
  • main 方法使用 Scanner 从标准输入读取一个字符串 s。
  • 然后调用 replaceNumber 方法来处理字符串,并打印返回的结果。
  • 最后,关闭 Scanner。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值