482. 密钥格式化 【1题多解】

582 阅读3分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

题目链接

482. 密钥格式化

描述

有一个密钥字符串 S ,只包含字母,数字以及 '-'(破折号)。其中, N 个 '-' 将字符串分成了 N+1 组。

给你一个数字 K,请你重新格式化字符串,使每个分组恰好包含 K 个字符。特别地,第一个分组包含的字符个数必须小于等于 K,但至少要包含 1 个字符。两个分组之间需要用 '-'(破折号)隔开,并且将所有的小写字母转换为大写字母。

给定非空字符串 S 和数字 K,按照上面描述的规则进行格式化。

测试用例

输入:S = "5F3Z-2e-9-w", K = 4
输出:"5F3Z-2E9W"
解释:字符串 S 被分成了两个部分,每部分 4 个字符;
     注意,两个额外的破折号需要删掉。

分析

有一个字符串,包含大小写和数字,并且会被 - 不规则的隔开,需要我们去掉全部的 -,然后再按长度 k 隔开,即除第一组数据的长度 ≤K,其余组的数据长度均为 k,当然,题目要求我们最后返回全部大写的数据

解法 1

  1. 用正则去掉 -
  2. 字符串转化为数组
  3. 从后往前,计算数组的下标位置,插入 -
var licenseKeyFormatting = function(s, k) {
    s = s.toUpperCase().replace(/-/g,'').split('');
    for(let i=s.length  - k;i>0;i-=k){
        s.splice(i,0,'-');
    }
        return s.join('')
};
// 	272 ms	44.5 MB

缺点分析:

因为代码里使用了 Array.splice(),这个方法会返回删除数组,并修改原数组,占用了大量的耗时以及内存

解法 2

采用栈的结构,一进一出,判断插入 -

即在 解法1 的基础上,替换了中间的数组插入操作,挨个将数据入栈,同时判断并插入 -

var licenseKeyFormatting = function(s, k) {
        s = s.toUpperCase().replace(/-/g, '').split('');
        let i = 0;
        let arr = [];
        while (s.length > 0) {
                if (i++ == k) {
                        i = 1;
                        arr.unshift('-');
                }
                arr.unshift(s.pop());
        }
        return arr.join('')
};
// 532 ms	45.3 MB

这个解法有几个优化的点:

  1. 不预处理字符串
  2. 按位从后往前读取字符串(而不是现在的弹出操作),入栈到新变量
  3. 在上一步的基础上,需要判断插入 -,并需要忽略掉原字符串中的 -

解法 3

不借助额外结构,先处理为数组后,直接使用 reduceRight() 合并字符,判断插入 -

var licenseKeyFormatting = function(s, k) {
        s = s.toUpperCase().replace(/-/g, '').split('');
        let i = 0;
        return s.reduceRight((a, b) => {
                if (i++ == k) {
                        i = 1;
                        return b + '-' + a;
                }
                return b + a;
        }, '')
};
// 64 ms	43.4 MB

很明显,这种解法节省了不少空间和时间,相比上2种,性能飞跃了不少,但这3种解法,都是先预处理了字符串

解法 4

不切割字符串,直接倒序遍历按位读取并拼接,省去了转化为数组操作所需要的内存

var licenseKeyFormatting = function(s, k) {
        // s = s.toUpperCase().replace(/-/g, '').split('');
        let m = 0;
        let str = '';
        for (let i = s.length - 1; i >= 0; i--) {
                let cs = s.charAt(i);
                if (cs == '-') continue;
                if (m++ == k) {
                        m = 1;
                        str = cs + '-' + str;
                } else {
                        str = cs + str;
                }
        }
        return str.toUpperCase();
};
// 72 ms	42.7 MB
// 80 ms	42.4 MB

按理来说,这种解法已经是最优,空间占用已经最少了,但耗时还是比 解法3 略微多一点点


每一份努力,都是我们跳槽的资本,欢迎 hxd 一起来交流、相互督促、共同成长~