【LC75】No443. 压缩字符串

题目描述

给你一个字符数组 chars ,请使用下述算法压缩:

从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符

  • 如果这一组长度为 1 ,则将字符追加到 s 中。

  • 否则,需要向 s 追加字符,后跟这一组的长度。

压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,如果组长度为 1010 以上,则在 chars 数组中会被拆分为多个字符。

请在 修改完输入数组后 ,返回该数组的新长度。

你必须设计并实现一个只使用常量额外空间的算法来解决此问题。

示例

示例 1:

输入:chars = ["a","a","b","b","c","c","c"]
输出:返回 6 ,输入数组的前 6 个字符应该是:["a","2","b","2","c","3"]
解释:"aa" 被 "a2" 替代。"bb" 被 "b2" 替代。"ccc" 被 "c3" 替代。

示例 2:

输入:chars = ["a"]
输出:返回 1 ,输入数组的前 1 个字符应该是:["a"]
解释:唯一的组是“a”,它保持未压缩,因为它是一个字符。

示例 3:

输入:chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
输出:返回 4 ,输入数组的前 4 个字符应该是:["a","b","1","2"]。
解释:由于字符 "a" 不重复,所以不会被压缩。"bbbbbbbbbbbb" 被 “b12” 替代。

链接

https://leetcode.cn/problems/string-compression/description/?envType=study-plan-v2&envId=leetcode-75

思路

依旧是很好理解的题目,没有背景设置,所以中译中环节省略。

但是是不会做题的一天orz

题目要求“只使用常量额外空间”,但要最后要压缩成一个新的数组,因此我们只能在原数组 chars 中进行覆写。

我们可以比较容易地想到双指针法。(根据题目要求也不太好想到别的方法

和普通双指针不同的是,由于此题需要覆写原数组,所以需要额外增加一个指针用来作为写入位置的标识。

解法一:双指针

使用一个指针 start 作为每组相同字符的起始标识;使用一个指针 read 作为遍历标识,在循环中寻找每组相同字符的结尾,那么此时 read - start + 1 即为这组相同字符的数量;使用一个指针 write 作为写入标识,用来指示覆写到了什么位置。

此处 read 查找的是每组相同字符的结尾,而不是下一个字符的开头,是因为最后一组相同字符可能不存在下一个字符。例如 ["a","a","b","b","c","c","c"] 中,最后一组 c 直接重复到了数组结尾,不存在下一个字符。如果使用找到下一个字符开头的判断条件,计算字符数量公式为 read - start,而最后一组 c 没有下一个元素,会导致计算结果少一个。

在根据题意将数量写入数组时,需要将数字按位拆开再写入。我们可以使用取余和除法计算出该数字的每一位,但此方法是从个位开始算起,所以在写入数组之后需要将这段元素反转。

代码

class Solution {
    public int compress(char[] chars) {
        int len = chars.length;
        // read为遍历标识,start为相同字符的起始位置,write为覆写位置标识
        int read = 0, write = 0, start = 0;
        while (read < len) {
            // 当找到相同字符的最后一个,或者遍历到数组结尾的时候
            if (read == len - 1 || chars[read] != chars[read + 1]) {
                // 先写入字母字符
                chars[write] = chars[read];
                write++;
                // 计算相同字符的数量
                int num = read - start + 1;
                if (num > 1) {
                    // 数量字符的起始位置,提供给反转时使用
                    int numStart = write;
                    // 从个位开始按位分割相同字符的数量
                    while (num > 0) {
                        chars[write] = (char) ((num % 10) + '0');
                        num /= 10;
                        write++;
                    }
                    // 反转数量部分的字符
                    reverse(chars, numStart, write - 1);
                }
                // 将起始标识移动到下一组字符的开头
                start = read + 1;
            }
            read++;
        }
        return write;
    }

    // 反转数组中下标起止位置为left和right部分的字符
    public void reverse(char[] chars, int left, int right) {
        while (left < right) {
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
    }
}
  • 时间复杂度:O(n)

  • 空间复杂度:O(1)

解法二:双指针但不用反转版

我们也可以使用 java 中类的内置方法,将计算出来的 int 数量,转化为 String 字符串,之后直接调用方法转化为字符数组,这样省去了反转的步骤。

代码

class Solution {
    public int compress(char[] chars) {
        int len = chars.length;
        // read为遍历标识,start为相同字符的起始位置,write为覆写位置标识
        int read = 0, write = 0, start = 0;
        while (read < len) {
            // 当找到相同字符的最后一个,或者遍历到数组结尾的时候
            if (read == len - 1 || chars[read] != chars[read + 1]) {
                // 先写入字母字符
                chars[write] = chars[read];
                write++;
                // 计算相同字符的数量
                int num = read - start + 1;
                if (num > 1) {
                    // 使用内置方法转化数字为字符数组
                    for (char c : String.valueOf(num).toCharArray()) {
                        chars[write] = c;
                        write++;
                    }
                }
                // 将起始标识移动到下一组字符的开头
                start = read + 1;
            }
            read++;
        }
        return write;
    }
}
  • 时间复杂度:O(n)

  • 空间复杂度:O(1)


【LC75】No443. 压缩字符串
https://tiamo495.com//archives/lc75-no443.-ya-suo-zi-fu-chuan
作者
tiamo495
发布于
2025年03月20日
许可协议