LeetCode 394:字符串解码——为什么遇到括号要保存现场

题目描述

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

编码规则是:

k[encoded_string]

表示方括号内部的 encoded_string 正好重复 k 次。

例如:

3[a]        -> aaa
3[a2[c]]    -> accaccacc
2[abc]3[cd]ef -> abcabccdcdcdef

题目保证输入字符串有效,括号一定匹配,数字只表示重复次数,不会出现 3a2[4] 这种情况。

最初卡住的地方:嵌套结构不知道怎么处理

这题如果只有一层括号,比如:

3[a]

很容易看出答案是把 a 重复 3 次。

但一旦出现嵌套:

3[a2[c]]

就不能简单地找第一个 [ 和第一个 ]。因为 2[c] 是内层,要先解出 cc,再和外层的 a 组合成 acc,最后再整体重复 3 次。

所以这题的关键不是“怎么重复字符串”,而是:进入一层新括号时,如何保存上一层还没处理完的状态。

正确思路:用栈保存现场

遇到 [ 时,可以理解为进入了一个新的子问题。进入新子问题之前,需要保存当前层的两个信息:

  • 当前已经拼好的字符串 cur
  • 当前读到的重复次数 num

这两个信息就是“现场”。等遇到 ] 时,当前层处理完了,再从栈里恢复上一层现场。

遍历字符串时维护:

  • num:当前读到的重复次数,可能是多位数。
  • cur:当前这一层正在构造的字符串。
  • stack:保存每次进入 [ 前的 curnum

四种字符的处理方式

遍历字符串时,字符只需要分成四类处理。

1. 遇到数字

数字表示后面括号内容的重复次数。因为重复次数可能是多位数,所以要累积:

num = num * 10 + 当前数字

例如 10[a] 中,先读到 1,再读到 0,最终 num 应该是 10,而不是 0 或 1。

2. 遇到 [

说明马上要进入一层新的括号。

此时要做三件事:

保存当前 cur 和 num
cur 清空
num 归零

这一步很重要。因为括号内部是一个新的字符串构造过程,不能继续沿用外层的 curnum

3. 遇到字母

字母属于当前层,直接拼到 cur 后面:

cur = cur + 当前字母

4. 遇到 ]

说明当前括号处理结束。

这时从栈里弹出上一层的字符串和重复次数:

prev = 上一层字符串
k = 上一层重复次数
cur = prev + 当前 cur 重复 k 次

这样就把当前层的结果合并回上一层。

手推过程

3[a2[c]] 为例。

初始状态:

num = 0
cur = ""
stack = []
当前字符操作curnumstack
3累积数字""3[]
[保存现场,进入新层""0[("", 3)]
a拼接字母"a"0[("", 3)]
2累积数字"a"2[("", 3)]
[保存现场,进入新层""0[("", 3), ("a", 2)]
c拼接字母"c"0[("", 3), ("a", 2)]
]弹出 ("a", 2),合并"acc"0[("", 3)]
]弹出 ("", 3),合并"accaccacc"0[]

最终答案:

accaccacc

我的原始错因

这题最容易漏的一步是:遇到 [ 后只把当前状态入栈,但没有重置 curnum

如果不重置 num,在 3[a2[c]] 中读到外层的 3 后,遇到内层的 2 时就可能被错误地算成:

3 * 10 + 2 = 32

但内层真正的重复次数应该是 2。

因此,遇到 [ 后必须同时完成:

入栈当前现场
cur = ""
num = 0

另一个小问题是:

num = num * 10 + cn % 10

这里的 % 10 没有必要,因为 c - '0' 本身已经是当前数字。

伪代码

创建 stack
num = 0
cur = ""

遍历 s 中的每个字符 c:
    如果 c 是数字:
        num = num * 10 + 当前数字

    如果 c 是 '[':
        把 cur 和 num 保存到 stack
        cur = ""
        num = 0

    如果 c 是字母:
        cur 拼接 c

    如果 c 是 ']':
        从 stack 弹出 prev 和 k
        cur = prev + cur 重复 k 次

返回 cur

Java 代码

import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    class Node {
        String str;
        int k;

        Node(String str, int k) {
            this.str = str;
            this.k = k;
        }
    }

    public String decodeString(String s) {
        Deque<Node> stack = new ArrayDeque<>();
        int num = 0;
        String cur = "";

        for (char c : s.toCharArray()) {
            if (c >= '0' && c <= '9') {
                num = num * 10 + (c - '0');
            } else if (c == '[') {
                stack.push(new Node(cur, num));
                cur = "";
                num = 0;
            } else if (c >= 'a' && c <= 'z') {
                cur += c;
            } else if (c == ']') {
                Node prev = stack.pop();

                String repeated = "";
                for (int i = 0; i < prev.k; i++) {
                    repeated += cur;
                }

                cur = prev.str + repeated;
            }
        }

        return cur;
    }
}

这份代码保留了最直观的写法:Node 记录进入 [ 前的字符串和重复次数。重点是遇到 [ 后一定要重置 curnum,否则嵌套时状态会串到下一层。

建议测试用例

3[a]             -> aaa

这个用例检查最基础的一层重复。

3[a2[c]]         -> accaccacc

这个用例检查嵌套括号能否正确先处理内层。

2[abc]3[cd]ef    -> abcabccdcdcdef

这个用例检查多个编码片段和普通字母能否拼接到一起。

10[a]            -> aaaaaaaaaa

这个用例检查多位数是否正确处理。

复杂度分析

  • 时间复杂度:O(n)O(输出长度)。每个输入字符都会被遍历一次,字符串重复时还需要生成最终结果。
  • 空间复杂度:O(n)。栈中保存嵌套括号之前的字符串状态,最终结果本身也需要空间。

总结

这道题的核心是“括号嵌套 = 保存现场”。

遇到 [ 时,把上一层的字符串和重复次数保存起来,然后开始处理新的一层;遇到 ] 时,说明当前层结束,把当前层结果重复指定次数后合并回上一层。

写这题时重点检查两件事:第一,数字要支持多位数;第二,遇到 [ 后一定要重置 curnum。只要这两个状态处理清楚,嵌套结构就不会乱。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值