代码随想录算法训练营第八天 | 344. 反转字符串、541. 反转字符串II、替换数字、151. 反转字符串中的单词、55. 右旋字符串

344. 反转字符串

leetcode
文档讲解
视频讲解

很明显的双指针解法

class Solution {
    public void reverseString(char[] s) {
        int left = 0, right = s.length - 1;
        char tmp;
        while (left < right) {
            tmp = s[left];
            s[left] = s[right];
            s[right] = tmp;
            left++;
            right--;
        }
    }
}

541. 反转字符串 II

leetcode
文档讲解
视频讲解

每次处理从下标 i 开始的前 k 个字符即可,i 从 0 开始,每次移动 2k 个位置。

class Solution {
    public String reverseStr(String s, int k) {
        char[] str = s.toCharArray();

        // i 从 0 开始,每次移动 2k 个位置
        for (int i = 0; i < s.length(); i += 2 * k) {
            // 反转从 i 开始的前 k 个元素,区间下标是 [i, i + k - 1]
            if (i + k - 1 < s.length()) {
                reverse(str, i, i + k - 1);
            } else {
                reverse(str, i, s.length() - 1);
            }
        }

        return new String(str);
    }

    public void reverse(char[] s, int i, int j) {
        while (i < j) {
            char tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;
            i++;
            j--;
        }
    }
}

卡码网 - 54. 替换数字

卡码网
文档题解

题目本身理解起来并不难,无非是遍历字符串,找到数字字符,然后将其替换成 "number",难的是如何进行数组的操作。像 C 语言这种使用字符数组表示字符串,由于数组大小是固定的,所以得先统计字符串中数字字符的数量,然后按照替换后的字符串的长度申请新数组。这里有个巧妙的方法就是从后往前填充数组,这样可以避免插入元素时数组元素的挨个移动。

function replaceDigit(str) {
    // 统计字符串数组中数字字符的数量
    let count = 0;
    for (let i = 0; i < str.length; i++) {
        count++;
    }

    newStr = new Array(count * 6);
    let left = str.length - 1;
    let right = newStr.length - 1;

    while (left >= 0) {
        if (isNaN(Number(str[left], 10))) {
            newStr[right] = str[left];
        } else {
            newStr[right--] = 'r';
            newStr[right--] = 'e';
            newStr[right--] = 'b';
            newStr[right--] = 'm';
            newStr[right--] = 'u';
            newStr[right] = 'n';
        }
        left--;
        right--;
    }

    return newStr;
}

对于像 Java 这种语言,字符串是不能修改的,所以需要 StringBuilder 来操作字符串,思路和上面不一样。

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        String s = sc.nextLine();
        StringBuilder sd = new StringBuilder();

        for (int i = 0; i < s.length(); i++) {
            if (Character.isDigit(s.charAt(i))) {
                sd.append("number");
            } else {
                sd.append(s.charAt(i));
            }
        }

        System.out.println(sd.toString());

        sc.close();
    }
}

151. 反转字符串中的单词

leetcode
文档题解
视频题解

思路:

  1. 先去掉字符串中的空格(前后空格 + 单词间多余的空格)
  2. 再将整个字符串反转(参考前面的题目)
  3. 再将各个单词再反转回来(小范围的反转字符串,体力活)
#include <string.h>

// 反转字符串
void reverseString(char* s, int i, int j) {
    while (i < j) {
        char tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        i++;
        j--;
    }
}

// 去掉额外的空格,类似于 27. 移除元素,一个思想
void removeExtraSpace(char* s) {
    int i = 0;
    int j = 0;

    // 用 j 遍历字符串,j 是在查找单词的开头
    while (j < strlen(s)) {
        // 当前字符不是空格,说明找到了一个单词的开头
        if (s[j] != ' ') {
            // 重点:i != 0,说明当前已经不是第一个单词了,所以要在该单词前面加空格
            if (i != 0) {
                s[i] = ' ';
                i++;
            }
            // 重点:然后将单词的各个字母逐个复制给 i
            while (j < strlen(s) && s[j] != ' ') {
                s[i] = s[j];
                i++;
                j++;
            }
        } else {
            // 重点:当前字符是空格,j 往后找
            j++;
        }
    }

    // i 就是去除空格后字符串的长度,也是结束符的位置
    s[i] = '\0';
}

char* reverseWords(char* s) {
    removeExtraSpace(s);
    reverseString(s, 0, strlen(s) - 1);
    int start = 0;
    for (int i = 0; i <= strlen(s); i++) {
        // 遇到空格,将前面的单词反转
        if (s[i] == ' ' || s[i] == '\0') {
            reverseString(s, start, i - 1);
            start = i + 1;
        }
    }
    return s;
}

55. 右旋字符串

卡码网
文档题解

暴力思路,每次将最后一个元素插到最前面,类似于 “头插法”,当然,前面的元素得挨个后移。每次 “头插” 一个元素,都要移动前面的 n - 1 个元素,总共移动 k (n - 1) 次,所以时间复杂度是 O(k n)。如果 k 等于 n 的话,那时间复杂度就是 O(n^2),所以最好时间复杂度是 O(1),即 k 等于 0,最坏时间按复杂度是 O(n^2),即 k 等于 n。

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int k = sc.nextInt();
        sc.nextLine();
        String str = sc.nextLine();
        char[] arr = str.toCharArray();

        // 取最后一个元素
        char tmp = arr[arr.length - 1];
        while (k > 0 && k <= arr.length) {
            // 前面的元素后移
            for (int i = arr.length - 1; i > 0; i--) {
                // 后移,即低位覆盖高位
                arr[i] = arr[i - 1];
            }
            // 头插法
            arr[0] = tmp;
            // 取最后一个元素
            tmp = arr[arr.length - 1];
            k--;
        }

        System.out.println(new String(arr));

        sc.close();
    }
}

还有一个比较巧妙的 O(n) 的解法。那就是先整个反转,然后再将两部分反转,如 [a b c d e f g] 整个反转为 [g f e d c b a],然后再将两部分 [g f]、[e d c b a] 反转为 [f g]、[a b c d e]。基础都是反转字符串的代码。和 LC 151 反转单词 类似,一步步反转。

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int k = sc.nextInt();
        sc.nextLine();
        String str = sc.nextLine();
        char[] arr = str.toCharArray();

        if (k > 0 && k <= arr.length) {
            // 先整个反转
            reverse(arr, 0, arr.length - 1);
            // 然后再部分反转
            reverse(arr, 0, k - 1);
            reverse(arr, k, arr.length - 1);
        }

        System.out.println(new String(arr));

        sc.close();
    }

    public static void reverse(char[] s, int i, int j) {
        while (i < j) {
            char tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;
            i++;
            j--;
        }
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注