443. 压缩字符串 - 力扣(LeetCode)
文章起笔:2021年11月6日21:22:32
问题描述及示例
给你一个字符数组 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”]
解释:
没有任何字符串被替代。
示例 3:
输入:chars = [“a”,“b”,“b”,“b”,“b”,“b”,“b”,“b”,“b”,“b”,“b”,“b”,“b”]
输出:返回 4 ,输入数组的前 4 个字符应该是:[“a”,“b”,“1”,“2”]。
解释:
由于字符 “a” 不重复,所以不会被压缩。“bbbbbbbbbbbb” 被 “b12” 替代。
注意每个数字在数组中都有它自己的位置。
提示:
1 <= chars.length <= 2000
chars[i] 可以是小写英文字母、大写英文字母、数字或符号
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/string-compression
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解
一开始我是想用一个 Map
类型的变量(假设变量名为 map
)来存储字符与其个数的映射关系。然后在遍历 chars
的过程中不断地累积相同字符的个数,但是写完之后我突然发现这种想法是错误的,因为题目中是只对 连续重复字符 进行压缩,而如果利用 Map
类型的变量来记录字符的数量的话,就会导致某些不连续的重复字符被错误累积。
比如输入:chars = ["a","a","b","b","a","a","a","c","c","c"]
,那么遍历完 chars
之后,在 map
中就会记录以下结果:
Map(3) {"a" => 5, "b" => 2, "c" => 3}
[[Entries]]
0: {"a" => 5}
1: {"b" => 2}
2: {"c" => 3}
size: 3
利用 entries()
变量进行遍历之后,就可以得到以下结果:
let arr = [...map.entries()];
arr
(3) [Array(2), Array(2), Array(2)]
0: (2) ["a", 5]
1: (2) ["b", 2]
2: (2) ["c", 3]
length: 3
这显然不是我们预期的压缩效果,因为前面的两个连续 a
和后面的三个连续 a
是不连续的。
成功前的尝试
于是后来我改变了思路。我用两个指针分别指向某个连续重复字符子串的左右边界。其中 front
指向前边界,而 i
指针指向后边界。同时 i
指针也担当着遍历 chars
数组的任务。
在遍历 chars
的过程中,如果发现当前字符是某个连续重复字符子串的的后边界,就需要将当前的这个连续重复字符子串按照规则进行压缩,这里运用了关键的 splice()
这个数组方法来完成对原数组的改变。
这里一定要注意
splice()
函数会改变原数组,也就是说,在进行压缩操作后,可能会出现“下标塌陷”的现象。所以在使用splice()
后一定要对i
指针的位置做调整以手动改变遍历的节奏。
但是一开始我没有考虑清楚处理“下标塌陷”的逻辑,只是考虑到了重复字符的数量是一位数或两位数的情况,如果某个连续重复字符子串的字符数量超过两位就会出现问题。
// 我一开始处理下标缺陷的方案,没有考虑重复字符的位数超过两位的情况
i = i - front + 1 < 10 ? front + 1 : front + 2;
执行结果:解答错误
通过测试用例:71 / 72
输入:["a","a","a","a",...,"a","a","a","a","a","a"](这里有1000个连续的 "a")
输出:["a","1","0","0","2"]
预期结果:["a","1","0","0","0"]
时间:2021/11/06 21:27
成功解决下标塌陷的问题
在浏览器里调试了一番后,我发现了上面的问题所在,所以修改了相应的处理“下标塌陷”的逻辑。
详解请看下方注释:
/**
* @param {character[]} chars
* @return {number}
*/
var compress = function(chars) {
// front指针用于标识一个连续重复字符子串的前边界,其初始指向chars头部
let front = 0;
// 开始遍历chars数组
for(let i = 0; i < chars.length; i++) {
// 如果当前遍历字符和下一个待遍历字符是一样的,说明当前遍历字符不是目标子串的后边界
if(chars[i] === chars[i + 1]) {
// 跳过后面的逻辑
continue;
}
// 如果发现当前遍历字符是某个子串的后边界,就要开始实现压缩逻辑了
// 如果发现当前连续重复字符子串的长度为1,则只需要改变front的指向即可
if(i - front === 0) {
front = i + 1;
} else if(i - front === 1) {
// 如果发现当前连续重复字符子串的长度为2,则只需要将当前遍历字符改为'2',
// 这样可以省去不必要的splice()操作,因为splice()操作比较耗费性能,能省则省
chars[i] = '2';
} else {
// 如果发现当前连续重复字符子串的长度为超过2,则需要将计算当前连续重复字符子串的长度
// 并且还要将计算所得的长度(数值类型)分割为一个个字符,
let count = (i - front + 1).toString().split('');
// 将上面计算所得的长度拼接到原子串的后面,注意这里使用了扩展运算符
chars.splice(front + 1, i - front, ...count);
// 这种情况下需要特别处理一下数组下标塌陷的问题
i = front + count.length;
}
// 压缩完一个子串后,需要移动front指针的位置来为下一个子串的压缩做准备
front = i + 1;
}
// 按照题目要求,最后还要返回被修改后的chars的数组的长度
return chars.length;
};
提交记录
执行结果:通过
72 / 72 个通过测试用例
执行用时:72 ms, 在所有 JavaScript 提交中击败了90.72%的用户
内存消耗:39.6 MB, 在所有 JavaScript 提交中击败了73.53%的用户
时间:2021/11/06 21:30
在LeetCode的某些题目中,可能会要求直接在传入的参数上做修改,而不要特意返回某个值,这个时候就要注意不要对传入的参数做内存地址重新赋值的操作,那样的话可能虽然逻辑对了,但是就是不能通过提交。
官方题解
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
【更新结束】
更新:2021年11月6日21:27:27
【更新结束】
有关参考
暂无