leetcode
leetcode 601 ~ 650
原子的数量

原子的数量

难度:

标签:

题目描述

代码结果

运行时间: 29 ms, 内存: 0.0 MB


/*
 * 题目思路:
 * 1. 与上述 Java 实现思路类似,依旧使用栈来处理括号嵌套问题。
 * 2. 使用 Java Stream 来对最终结果进行排序和拼接。
 */
import java.util.*;
import java.util.stream.Collectors;
 
public class AtomCounterStream {
    public String countOfAtoms(String formula) {
        Map<String, Integer> map = new HashMap<>();
        Stack<Map<String, Integer>> stack = new Stack<>();
        int i = 0, n = formula.length();
        while (i < n) {
            if (formula.charAt(i) == '(') {
                stack.push(map);
                map = new HashMap<>();
                i++;
            } else if (formula.charAt(i) == ')') {
                int iStart = ++i;
                int multiplicity = 0;
                while (i < n && Character.isDigit(formula.charAt(i))) {
                    multiplicity = multiplicity * 10 + formula.charAt(i++) - '0';
                }
                multiplicity = multiplicity == 0 ? 1 : multiplicity;
                if (!stack.isEmpty()) {
                    Map<String, Integer> top = map;
                    map = stack.pop();
                    for (String key : top.keySet()) {
                        map.put(key, map.getOrDefault(key, 0) + top.get(key) * multiplicity);
                    }
                }
            } else {
                int iStart = i++;
                while (i < n && Character.isLowerCase(formula.charAt(i))) i++;
                String name = formula.substring(iStart, i);
                int multiplicity = 0;
                while (i < n && Character.isDigit(formula.charAt(i))) {
                    multiplicity = multiplicity * 10 + formula.charAt(i++) - '0';
                }
                multiplicity = multiplicity == 0 ? 1 : multiplicity;
                map.put(name, map.getOrDefault(name, 0) + multiplicity);
            }
        }
        return map.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .map(entry -> entry.getKey() + (entry.getValue() > 1 ? entry.getValue().toString() : ""))
            .collect(Collectors.joining());
    }
}

解释

方法:

这个题解使用了栈的数据结构来解决问题。它从左到右遍历化学式字符串,对于每个字符进行分情况讨论: 1. 如果是左括号,将当前原子入栈,并清空当前原子。 2. 如果是右括号,将当前原子入栈,并清空当前原子。 3. 如果是数字,判断前一个字符是否也是数字,如果是则继续遍历;否则将当前原子和数字作为一个元组入栈,并清空当前原子。如果前一个字符是右括号,则将数字作为括号内原子的系数,将括号内的所有原子依次出栈并乘以系数后重新入栈。 4. 如果是小写字母,将其加入当前原子的名称中。 5. 如果是大写字母,将当前原子入栈,并将该大写字母作为新的当前原子。 遍历完成后,将栈中的所有原子及其数量存入哈希表中,最后按照字典序将哈希表中的原子拼接成字符串返回。

时间复杂度:

O(nlogn)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中提到如果当前字符是数字且前一个字符是右括号时,你是如何处理括号内的所有原子乘以系数的?请详细解释这个过程。
当遇到一个数字且其前一个字符是右括号时,这个数字表示括号内所有原子的倍数。算法首先会将这个数字存储为一个系数。然后,使用一个临时栈,逐一将括号内的原子(在'('之前)出栈,每个原子的数量乘以这个系数。出栈的过程会持续到遇到左括号为止,这标志着一个括号表达式的开始。处理完毕后,将处理结果(原子和新的数量)重新入栈。这样,括号内的所有原子都会以正确的数量被考虑在内。
🦆
为什么在处理大写字母时,如果当前原子不为空,需要先将当前原子入栈再更新当前原子?这样的处理对结果有什么影响?
在遍历化学式字符串时,每当遇到一个大写字母,它标志着一个新原子的开始。如果在此之前已经累积了一个原子的名称(即当前原子不为空),则需要先将这个原子及其数量(默认为1,除非紧随其后的是数字)入栈,然后再更新当前原子为新遇到的大写字母。这种处理确保了每个原子及其计数被正确记录并存储,以便于后续的数字处理或输出构建。如果不这样做,将导致原子计数错误或者原子名的覆盖,进而影响最终结果的正确性。
🦆
在解析数字时,你是如何确定数字的完整性并正确解析出整个数字的?这里的逻辑是否能正确处理所有的数字情况,例如连续的数字?
算法在遇到一个数字字符时,不会立即处理,而是检查后续字符是否也是数字,以此确保能够读取完整的数字(可能是多位数)。这通过初始化一个索引`j`为当前数字字符的下一位置开始,然后向后遍历,直到遇到非数字字符停止。这样就能正确解析出完整的数字。此逻辑能够准确处理包括连续数字在内的所有情况,确保不会因为数字的多位性而出错。
🦆
在处理完所有字符后,你是如何从栈中构建哈希表的?请解释这一步骤中的具体逻辑及其对最终结果的影响。
在字符串遍历完成后,算法遍历栈中的所有元素来构建一个哈希表,用于记录每个原子及其累积的数量。此过程中,算法检查栈中的每个元素,如果不是左括号(即是原子和数量的元组),则将原子名作为键,数量作为值添加到哈希表中。如果哈希表已存在该原子,其数量会累加。最后,根据哈希表中的数据按原子字典顺序生成最终的字符串。这一步骤确保了所有的原子计数被正确累加,并且按照要求的格式输出,是构建最终结果的关键。

相关问题

字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

 

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

 

提示:

  • 1 <= s.length <= 30
  • s 由小写英文字母、数字和方括号 '[]' 组成
  • s 保证是一个 有效 的输入。
  • s 中所有整数的取值范围为 [1, 300] 

编码最短长度的字符串

编码最短长度的字符串