【LC75】No443. 压缩字符串
题目描述
给你一个字符数组 chars
,请使用下述算法压缩:
从一个空字符串 s
开始。对于 chars
中的每组 连续重复字符 :
如果这一组长度为
1
,则将字符追加到s
中。否则,需要向
s
追加字符,后跟这一组的长度。
压缩后得到的字符串 s
不应该直接返回 ,需要转储到字符数组 chars
中。需要注意的是,如果组长度为 10
或 10
以上,则在 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” 替代。
链接
思路
依旧是很好理解的题目,没有背景设置,所以中译中环节省略。
但是是不会做题的一天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)