LeetCode 443. String Compression【字符串/双指针】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

Given an array of characters chars, compress it using the following algorithm:

Begin with an empty string s. For each group of consecutive repeating characters in chars:

  • If the group's length is 1, append the character to s.
  • Otherwise, append the character followed by the group's length.

The compressed string s should not be returned separately, but instead be stored in the input character array chars. Note that group lengths that are 10 or longer will be split into multiple characters in chars.

After you are done modifying the input array, return the new length of the array.

You must write an algorithm that uses only constant extra space.

Example 1:

Input: chars = ["a","a","b","b","c","c","c"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]
Explanation: The groups are "aa", "bb", and "ccc". This compresses to "a2b2c3".

Example 2:

Input: chars = ["a"]
Output: Return 1, and the first character of the input array should be: ["a"]
Explanation: The only group is "a", which remains uncompressed since it's a single character.

Example 3:

Input: chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
Output: Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"].
Explanation: The groups are "a" and "bbbbbbbbbbbb". This compresses to "ab12". 

Example 4:

Input: chars = ["a","a","a","b","b","a","a"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","3","b","2","a","2"].
Explanation: The groups are "aaa", "bb", and "aa". This compresses to "a3b2a2". Note that each group is independent even if two groups have the same character.

Constraints:

  • 1 <= chars.length <= 2000
  • chars[i] is a lower-case English letter, upper-case English letter, digit, or symbol.

题意:给你一个字符数组 chars ,用算法压缩,从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符

  • 如果这一组长度为 1 ,则将字符追加到 s 中。
  • 否则,需要向 s 追加字符,后跟这一组的长度。

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

请在 修改完输入数组后 ,返回该数组的新长度。必须设计并实现一个只使用常量额外空间的算法来解决此问题。


解法 双指针

这道题其实很简单,就是实现长程压缩算法:

  • 用两个指针 i, ji, j 一开始指向连续相同字符 chars[i] 的起点,同时用 ans = 0 记录压缩后数组的长度;
  • j 不断往后遍历,直到遇到不同字符 char[j] 或到了 chars[] 的末尾,此时这一组字符的长度 len = j - i
  • 将字符 chars[i] 写入 chars[ans++] 中;
  • 长度大于 1 时,先用 l 记录此时的位置 ans ,再顺序从个位到最高位将 len 写入 chars[ans++] ,然后将 [l, ans - 1] 区间的数字逆序;
  • 最后 i = j ,继续下一轮循环。

这无疑是最自然的想法,时间复杂度为 O ( n ) O(n) O(n) ,空间复杂度为 O ( 1 ) O(1) O(1) 。代码实现如下:

class Solution {
public:
    int compress(vector<char>& chars) { 
        int ans = 0;
        for (int i = 0, n = chars.size(); i < n; ) {
            int j = i;
            while (j < n && chars[j] == chars[i]) ++j;
            int len = j - i;
            chars[ans++] = chars[i];
            if (len > 1) {
                int l = ans;
                while (len) {
                    chars[ans++] = '0' + len % 10;;
                    len /= 10;
                }
                int r = ans - 1;
                while (l < r) swap(chars[l++], chars[r--]);
            }
            i = j;
        }
        return ans;
    }
}; 
//执行用时:4 ms, 在所有 C++ 提交中击败了87.32% 的用户
//内存消耗:8.6 MB, 在所有 C++ 提交中击败了71.52% 的用户

上述算法在具体细节上还能稍微简化一下,我们实际上不需要指向连续相同字符起点的指针,下面的过程相当于把上述的 j 换成了 i

  • 先用指针 i 指向连续相同字符 chars[i] 的起点,同时用 ans = 0 记录压缩后数组的长度;
  • i 不断往后遍历,直到遇到不同字符 char[i + 1] 或到了 chars[] 的末尾,同时用 len 计数这一组字符的长度;
  • 将字符 chars[i] 写入 chars[ans++] 中;
  • 长度大于 1 时,先用 l 记录此时的位置 ans ,再顺序从个位到最高位将 len 写入 chars[ans++] ,然后将 [l, ans - 1] 区间的数字逆序;
  • len = 0 ,继续 i 的遍历。

代码实现如下:

class Solution {
public:
    int compress(vector<char>& chars) { 
        int ans = 0;
        for (int i = 0, len = 1, n = chars.size(); i < n; ++i, ++len) {
            if (i + 1 != n && chars[i] == chars[i + 1]) continue;
            chars[ans++] = chars[i];
            if (len > 1) {
                int l = ans;
                while (len) {
                    chars[ans++] = '0' + len % 10;
                    len /= 10;
                }
                int r = ans - 1;
                while (l < r) swap(chars[l++], chars[r--]);
            }
            len = 0;
        } 
        return ans;
    }
};
//执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
//内存消耗:8.5 MB, 在所有 C++ 提交中击败了91.39% 的用户
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

memcpy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值