Leetcode之482.密钥格式化

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode之482.密钥格式化相关的知识,希望对你有一定的参考价值。

题目

题目描述

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

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

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

示例 1:

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

示例 2:

输入:S = “2-5g-3-J”, K = 2
输出:“2-5G-3J”
解释:字符串 S 被分成了 3 个部分,按照前面的规则描述,第一部分的字符可以少于给定的数量,其余部分皆为 2 个字符。

提示:

  • S 的长度可能很长,请按需分配大小。K 为正整数。
  • S 只包含字母数字(a-z,A-Z,0-9)以及破折号’-’
  • S 非空

来源

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/license-key-formatting
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

解法1

class Solution {
    /**
     * <p>思路:Java的API</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,利用Java的API删除掉原字符串中的所有破折号,并且将其全部转换成大写字母</li>
     *     <li>第二步,倒序循环遍历字符串,每隔K个字符则添加一个破折号</li>
     * </ul>
     * <p>结果:成功</p>
     * <ul>
     *     <li>执行用时:103 ms, 在所有 Java 提交中击败了8.97% 的用户</li>
     *     <li>内存消耗:39.1 MB, 在所有 Java 提交中击败了19.39% 的用户</li>
     *     <li>通过测试用例:38 / 38</li>
     * </ul>
     *
     * @param S 密钥字符串
     * @param K 数字K,使得每个分组恰好包含K个字符
     * @return 返回格式化后的密钥字符串
     */
    public String licenseKeyFormatting(String S, int K) {
        // 第一步,利用Java的API删除掉原字符串中的所有破折号,并且将其全部转换成大写字母
        S = S.replaceAll("-", "").toUpperCase();

        // 第二步,倒序循环遍历字符串,每隔K个字符则添加一个破折号
        StringBuilder result = new StringBuilder();// 保存结果的字符串
        char[] chars = S.toCharArray();// 将输入字符串S转换成字符数组
        int count = 0;// 计数器,记录已经遍历过的字符个数,此时字符串中已经不存在破折号字符了
        for (int i = chars.length - 1; i >= 0; i--) {
            // 记录字符个数
            count++;
            // 向StringBuilder中插入一个字符,注意使用头插法(即每个新字符插入到原字符串的头部)插入字符
            result.insert(0, chars[i]);
            // 当已经遍历K个字符后插入破折号字符,但需要注意当字符数刚好是K的整数倍,则不需要插入分隔字符符号,所以需要添加i!=0的判断
            if (count % K == 0 && i != 0) {
                result.insert(0, '-');
                // 同时将count置为0,开始下一次的记录
                count = 0;
            }
        }
        return result.toString();
    }
}

解法2

class Solution {

    /**
     * <p>思路:单指针倒序遍历</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,单指针,倒序遍历字符串,遇到破折号则跳过,同时将当前字符转换为大写字母插入到结果字符串中,当遍历K个字符后(不包括破折号),则插入一个破折号字符</li>
     * </ul>
     * <p>结果:成功</p>
     * <ul>
     *     <li>执行用时:91 ms, 在所有 Java 提交中击败了12.40% 的用户</li>
     *     <li>内存消耗:38.4 MB, 在所有 Java 提交中击败了79.16% 的用户</li>
     *     <li>通过测试用例:38 / 38</li>
     * </ul>
     *
     * @param S 密钥字符串
     * @param K 数字K,使得每个分组恰好包含K个字符
     * @return 返回格式化后的密钥字符串
     */
    public String licenseKeyFormatting(String S, int K) {
        // 指针,指向字符串S中的下标,从字符串中的最后一个字符开始
        int i = S.length() - 1;
        // 计数器,记录字符串S中非破折号字符的个数
        int count = 0;
        // 保存格式化后的密钥字符串的结果
        StringBuilder result = new StringBuilder();
        // 倒序遍历字符串
        while (i >= 0) {
            // 获取当前正在遍历的字符
            char c = S.charAt(i);
            // 判断如果不是破折号字符则添加到result中
            if (c != '-') {
                // 非破折号字符个数加一
                count++;
                // 插入到result中,同时注意将小写字母字符转换成大写字母字符
                result.insert(0, c >= 'a' ? (char) (c - 32) : c);
                // 当插入K个非破折号字符后,向result中插入破折号字符"-"
                if (count == K && i != 0) {
                    result.insert(0, '-');
                    count = 0;
                }
            }
            // 继续遍历下一个字符
            i--;
        }
        // 处理s是"--a-a-a-a--"或"---"的特殊情况
        return (result.length() > 0 && result.charAt(0) == '-') ? result.substring(1) : result.toString();
    }
}

解法3

class Solution {

    /**
     * <p>思路:栈的先进后出特性。因为要倒序遍历字符串,所以符合先进后出的特性,可以采用栈来实现。</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,将所有非破折号字符并且转换成大写字母字符入栈</li>
     *     <li>第二步,将栈顶字符出栈,并且每K个字符用破折号字符进行分隔插入到result</li>
     * </ul>
     * <p>结果:成功</p>
     * <ul>
     *     <li>执行用时:100 ms, 在所有 Java 提交中击败了9.10% 的用户</li>
     *     <li>内存消耗:39 MB, 在所有 Java 提交中击败了28.36% 的用户</li>
     *     <li>通过测试用例:38 / 38</li>
     * </ul>
     *
     * @param S 密钥字符串
     * @param K 数字K,使得每个分组恰好包含K个字符
     * @return 返回格式化后的密钥字符串
     */
    public String licenseKeyFormatting(String S, int K) {
        // 第一步,将所有非破折号字符并且转换成大写字母字符入栈
        int i = 0;
        Stack stack = new Stack();
        while (i < S.length()) {
            char c = S.charAt(i);
            if (c != '-') {
                // 将非破折号字符,并且将小写字母转换成大写字母填入栈中
                stack.push(c >= 'a' ? (char) (c - 32) : c);
            }
            i++;
        }

        // 第二步,将栈顶字符出栈,并且每K个字符用破折号字符进行分隔插入到result
        StringBuilder result = new StringBuilder();
        int count = 0;// 已经插入到result字符串中字符的个数
        int len = stack.size();// 非破折号字符的个数
        // 循环遍历栈
        while (!stack.isEmpty()) {
            // 插入到result中的字符加1
            count++;
            // 将栈顶元素插入到result中,并且将栈顶元素出栈
            result.insert(0, stack.pop());
            // 每K个字符插入一个破折号字符
            if (count % K == 0 && count != len) {
                result.insert(0, '-');
            }
        }
        return result.toString();
    }
}

解法4

public class Solution {

    /**
     * <p>思路:数组,将字符存放到数组中,效率更高。</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,统计字符串中非破折号字符的个数,并且将小写字母字符转换成大写字母字符存入原数组</li>
     *     <li>第二步,倒序将chars数组中的非破折号字符向结果数组result赋值,并且每K个元素插入一个破折号字符</li>
     * </ul>
     * <p>结果:成功</p>
     * <ul>
     *     <li>执行用时:3 ms, 在所有 Java 提交中击败了100.00% 的用户</li>
     *     <li>内存消耗:38.6 MB, 在所有 Java 提交中击败了50.13% 的用户</li>
     *     <li>通过测试用例:38 / 38</li>
     * </ul>
     *
     * @param S 密钥字符串
     * @param K 数字K,使得每个分组恰好包含K个字符
     * @return 返回格式化后的密钥字符串
     */
    public String licenseKeyFormatting(String S, int K) {
        // 第一步,统计字符串中非破折号字符的个数,并且将小写字母字符转换成大写字母字符存入原数组
        int count = 0;// 字符串中非破折号字符的个数
        char[] chars = S.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] != '-') {
                // 统计非破折号字符的个数
                count++;
                // 将小写字母字符转换成大写字母字符存入原数组
                chars[i] = chars[i] >= 'a' ? (char) (chars[i] - 32) : chars[i];
            }
        }

        // 如果没有字符则返回空字符串
        if (count == 0) {
            return "";
        }

        // 计算分隔符的个数
        int separator = count / K;
        // 如果正好整除,分隔符个数 - 1
        separator = count % K == 0 ? separator - 1 : separator;

        // 第二步,倒序将chars数组中的非破折号字符向结果数组result赋值,并且每K个元素插入一个破折号字符
        char[] result = new char[count + separator];
        int index = result.length - 1;// 指向result数组的下标
        int letter = 0;// 统计已赋值的字符个数
        for (int i = chars.length - 1; i >= 0; i--) {
            // 处理非破折号字符
            if (chars[i] != '-') {
                // 将非破折号字符放入到result数组中
                result[index] = chars[i];
                index--;
                letter++;
                // 每K个元素插入一个破折号字符
                if (letter % K == 0 && index != -1) {
                    result[index] = '-';
                    index--;
                }
            }
        }
        return new String(result);
    }
}

以上是关于Leetcode之482.密钥格式化的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode刷题100天—482. 密钥格式化(字符)—day57

Leetcode刷题100天—482. 密钥格式化(字符)—day57

LeetCode 405. 数字转换为十六进制数(补码的问题) / 166. 分数到小数(模拟长除法) / 482. 密钥格式化

482. 密钥格式化

LeetCode 482 秘钥格式化[模拟 字符串] HERODING的LeetCode之路

《LeetCode之每日一题》:168.密钥格式化