# 443. 压缩字符串 - LeetCode Python/Java/C++/JS/C#/Go/Ruby 题解
访问原文链接:[443. 压缩字符串 - LeetCode Python/Java/C++/JS/C#/Go/Ruby 题解](https://leetcode.to/zh/leetcode/443-string-compression),体验更佳!
力扣链接:[443. 压缩字符串](https://leetcode.cn/problems/string-compression), 难度等级:**中等**。
## LeetCode “443. 压缩字符串”问题描述
给你一个字符数组 `chars` ,请使用下述算法压缩:
从一个空字符串 `s` 开始。对于 `chars` 中的每组 **连续重复字符** :
如果这一组长度为 `1` ,则将字符追加到 `s` 中。
否则,需要向 `s` 追加字符,后跟这一组的长度。
压缩后得到的字符串 `s` **不应该直接返回** ,需要转储到字符数组 `chars` 中。需要注意的是,如果组长度为 `10` 或 `10` 以上,则在 `chars` 数组中会被拆分为多个字符。
请在 **修改完输入数组后** ,返回该数组的新长度。
你必须设计并实现一个只使用常量额外空间的算法来解决此问题。
### [示例 1]
**输入**: `chars = ["a","a","b","b","c","c","c"]`
**输出**: `Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]`
**解释**:
"aa" 被 "a2" 替代。"bb" 被 "b2" 替代。"ccc" 被 "c3" 替代。
### [示例 2]
**输入**: `chars = ["a"]`
**输出**: `Return 1, and the first character of the input array should be: ["a"]`
**解释**: `唯一的组是“a”,它保持未压缩,因为它是一个字符。`
### [示例 3]
**输入**: `chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]`
**输出**: `Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"].`
**解释**:
由于字符 "a" 不重复,所以不会被压缩。"bbbbbbbbbbbb" 被 “b12” 替代。
### [约束]
- `1 <= chars.length <= 2000`
- `chars[i]` 可以是小写英文字母、大写英文字母、数字或符号
### [Hints]
提示 1
How do you know if you are at the end of a consecutive group of characters?
## 思路
我们使用双指针(一个读指针 `fast` 和一个写指针 `slow`)以及一个计数器 `count` 来实现原地压缩。
1. **初始化**:
- 在输入数组 `chars` 的末尾追加一个哨兵字符(例如空格 " ")。这能确保最后一组连续字符也能在循环内被正确处理。Java 和 C# 不适用,因为数组长度固定。
- `slow = 0`:`slow` 指针指向当前写入段的起始字符,它也是最终压缩数组的长度。
- `count = 1`:记录当前连续字符 `chars[slow]` 的出现次数。
2. **遍历与压缩**:
- `fast` 指针从索引 `1` 开始遍历数组 `chars`(直到哨兵字符)。
- **情况一:字符重复**(`chars[fast]` 等于 `chars[slow]`)
- 递增 `count`。
- **情况二:字符不重复**(`chars[fast]` 不等于 `chars[slow]`)
- **情况1:计数等于1**
- 请你实现相关逻辑
- **情况2:计数大于1**
- 请你实现相关逻辑
3. **返回结果**:
- 当 `fast` 指针遍历完整个数组(包括哨兵)后,`slow` 指针的值即为压缩后数组的新长度。
## “快慢指针技术”的模式
```java
int slow = 0; // slow pointer
// ...
for (int fast = 1; fast < array_length; fast++) { // 本行是关键!
if (condition_1) {
// ...
continue; // 'continue' 比 'else' 好,因为 'else' 会引入更多的缩进空格!
}
if (condition_2) {
// ...
continue; // 'continue' 比 'else' 好
}
// condition_3
// ...
slow++;
// ...
}
return something;
```
## 复杂度
- 时间复杂度: `O(N)`.
- 空间复杂度: `O(1)`.
## Python
```python
class Solution:
def compress(self, chars: List[str]) -> int:
chars.append(" ") # Append an extra special char to process the last char easier
slow = 0 # Slow pointer. This is the answer.
count = 1 # Count of consecutive repeating characters
for fast in range(1, len(chars)):
if chars[fast] == chars[slow]:
count += 1
continue # 'continue' is better than 'else' because 'else' will introduce more indents
if count == 1:
slow += 1
# Don't need to append the 'count' when count is 1.
chars[slow] = chars[fast]
continue # 'continue' is better than 'else' because 'else' will introduce more indents
# Append the 'count'
for c in str(count):
slow += 1
chars[slow] = c
slow += 1
chars[slow] = chars[fast]
count = 1
return slow
```
## Java
```java
import java.util.ArrayList;
import java.util.List;
class Solution {
public int compress(char[] chars) {
int slow = 0; // Slow pointer. This is the answer.
int count = 1; // Count of consecutive repeating characters
for (int fast = 1; fast <= chars.length; fast++) { // it is "<=", not "<"
var charFast = (fast == chars.length ? ' ' : chars[fast]);
if (charFast == chars[slow]) {
count++;
continue; // 'continue' is better than 'else' because 'else' will introduce more indents
}
if (count == 1) {
slow++;
// Don't need to append the 'count' when count is 1.
if (slow < chars.length) {
chars[slow] = charFast;
}
continue; // 'continue' is better than 'else' because 'else' will introduce more indents
}
// Append the 'count'
for (char c : String.valueOf(count).toCharArray()) {
slow++;
chars[slow] = c;
}
slow++;
if (slow < chars.length) {
chars[slow] = charFast;
}
count = 1;
}
return slow;
}
}
```
## C++
```cpp
class Solution {
public:
int compress(vector& chars) {
chars.push_back(' '); // Append an extra special char to process the last char easier
int slow = 0; // Slow pointer. This is the answer.
int count = 1; // Count of consecutive repeating characters
for (int fast = 1; fast < chars.size(); ++fast) {
if (chars[fast] == chars[slow]) {
count++;
continue; // 'continue' is better than 'else' because 'else' will introduce more indents
}
if (count == 1) {
slow++;
// Don't need to append the 'count' when count is 1.
chars[slow] = chars[fast];
continue; // 'continue' is better than 'else' because 'else' will introduce more indents
}
// Append the 'count'
for (char c : to_string(count)) {
slow++;
chars[slow] = c;
}
slow++;
chars[slow] = chars[fast];
count = 1;
}
return slow;
}
};
```
## C#
```csharp
public class Solution
{
public int Compress(char[] chars)
{
int slow = 0; // Slow pointer. This is the answer.
int count = 1; // Count of consecutive repeating characters
for (int fast = 1; fast <= chars.Length; fast++)
{ // it is "<=", not "<"
char charFast = (fast == chars.Length ? ' ' : chars[fast]);
if (charFast == chars[slow])
{
count++;
continue; // 'continue' is better than 'else' because 'else' will introduce more indents
}
if (count == 1)
{
slow++;
// Don't need to append the 'count' when count is 1.
if (slow < chars.Length)
{
chars[slow] = charFast;
}
continue; // 'continue' is better than 'else' because 'else' will introduce more indents
}
// Append the 'count'
foreach (char c in count.ToString())
{
slow++;
chars[slow] = c;
}
slow++;
if (slow < chars.Length)
{
chars[slow] = charFast;
}
count = 1;
}
return slow;
}
}
```
## JavaScript
```javascript
/**
* @param {character[]} chars
* @return {number}
*/
var compress = function(chars) {
chars.push(' ') // Append an extra special char to process the last char easier
let slow = 0 // Slow pointer. This is the answer.
let count = 1 // Count of consecutive repeating characters
for (let fast = 1; fast < chars.length; fast++) {
if (chars[fast] === chars[slow]) {
count++
continue // 'continue' is better than 'else' because 'else' will introduce more indents
}
if (count === 1) {
slow++
// Don't need to append the 'count' when count is 1.
chars[slow] = chars[fast]
continue // 'continue' is better than 'else' because 'else' will introduce more indents
}
// Append the 'count'
for (const c of count.toString()) {
slow++
chars[slow] = c
}
slow++
chars[slow] = chars[fast]
count = 1
}
return slow
}
```
## Go
```go
// A test cannot pass. Reason is still unknown
func compress(chars []byte) int {
chars = append(chars, ' ') // Append an extra special char to process the last char easier
slow := 0 // Slow pointer. This is the answer.
count := 1 // Count of consecutive repeating characters
for fast := 1; fast < len(chars); fast++ {
if chars[fast] == chars[slow] {
count++
continue // 'continue' is better than 'else' because 'else' will introduce more indents
}
if count == 1 {
slow++
// Don't need to append the 'count' when count is 1.
chars[slow] = chars[fast]
continue // 'continue' is better than 'else' because 'else' will introduce more indents
}
// Append the 'count'
for _, c := range strconv.Itoa(count) {
slow++
chars[slow] = byte(c)
}
slow++
chars[slow] = chars[fast]
count = 1
}
return slow
}
```
## Ruby
```ruby
# @param {Character[]} chars
# @return {Integer}
def compress(chars)
chars << " " # Append an extra special char to process the last char easier
slow = 0 # Slow pointer. This is the answer.
count = 1 # Count of consecutive repeating characters
(1...chars.length).each do |fast|
if chars[fast] == chars[slow]
count += 1
next # 'next' is better than 'else' because 'else' will introduce more indents
end
if count == 1
slow += 1
# Don't need to append the 'count' when count is 1.
chars[slow] = chars[fast]
next # 'next' is better than 'else' because 'else' will introduce more indents
end
# Append the 'count'
count.to_s.each_char do |c|
slow += 1
chars[slow] = c
end
slow += 1
chars[slow] = chars[fast]
count = 1
end
slow
end
```
## Other languages
```java
// Welcome to create a PR to complete the code of this language, thanks!
```
亲爱的力扣人,为了您更好的刷题体验,请访问 [LeetCode.to](https://leetcode.to/zh)。
本站敢称力扣题解最佳实践,终将省你大量刷题时间!
原文链接:[443. 压缩字符串 - LeetCode Python/Java/C++/JS/C#/Go/Ruby 题解](https://leetcode.to/zh/leetcode/443-string-compression).
GitHub 仓库: [leetcode-python-java](https://github.com/leetcode-python-java/leetcode-python-java).