题目描述
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则是:
k[encoded_string]
表示方括号内部的 encoded_string 正好重复 k 次。
例如:
3[a] -> aaa
3[a2[c]] -> accaccacc
2[abc]3[cd]ef -> abcabccdcdcdef
题目保证输入字符串有效,括号一定匹配,数字只表示重复次数,不会出现 3a 或 2[4] 这种情况。
最初卡住的地方:嵌套结构不知道怎么处理
这题如果只有一层括号,比如:
3[a]
很容易看出答案是把 a 重复 3 次。
但一旦出现嵌套:
3[a2[c]]
就不能简单地找第一个 [ 和第一个 ]。因为 2[c] 是内层,要先解出 cc,再和外层的 a 组合成 acc,最后再整体重复 3 次。
所以这题的关键不是“怎么重复字符串”,而是:进入一层新括号时,如何保存上一层还没处理完的状态。
正确思路:用栈保存现场
遇到 [ 时,可以理解为进入了一个新的子问题。进入新子问题之前,需要保存当前层的两个信息:
- 当前已经拼好的字符串
cur - 当前读到的重复次数
num
这两个信息就是“现场”。等遇到 ] 时,当前层处理完了,再从栈里恢复上一层现场。
遍历字符串时维护:
num:当前读到的重复次数,可能是多位数。cur:当前这一层正在构造的字符串。stack:保存每次进入[前的cur和num。
四种字符的处理方式
遍历字符串时,字符只需要分成四类处理。
1. 遇到数字
数字表示后面括号内容的重复次数。因为重复次数可能是多位数,所以要累积:
num = num * 10 + 当前数字
例如 10[a] 中,先读到 1,再读到 0,最终 num 应该是 10,而不是 0 或 1。
2. 遇到 [
说明马上要进入一层新的括号。
此时要做三件事:
保存当前 cur 和 num
cur 清空
num 归零
这一步很重要。因为括号内部是一个新的字符串构造过程,不能继续沿用外层的 cur 和 num。
3. 遇到字母
字母属于当前层,直接拼到 cur 后面:
cur = cur + 当前字母
4. 遇到 ]
说明当前括号处理结束。
这时从栈里弹出上一层的字符串和重复次数:
prev = 上一层字符串
k = 上一层重复次数
cur = prev + 当前 cur 重复 k 次
这样就把当前层的结果合并回上一层。
手推过程
以 3[a2[c]] 为例。
初始状态:
num = 0
cur = ""
stack = []
| 当前字符 | 操作 | cur | num | stack |
|---|---|---|---|---|
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
我的原始错因
这题最容易漏的一步是:遇到 [ 后只把当前状态入栈,但没有重置 cur 和 num。
如果不重置 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 记录进入 [ 前的字符串和重复次数。重点是遇到 [ 后一定要重置 cur 和 num,否则嵌套时状态会串到下一层。
建议测试用例
3[a] -> aaa
这个用例检查最基础的一层重复。
3[a2[c]] -> accaccacc
这个用例检查嵌套括号能否正确先处理内层。
2[abc]3[cd]ef -> abcabccdcdcdef
这个用例检查多个编码片段和普通字母能否拼接到一起。
10[a] -> aaaaaaaaaa
这个用例检查多位数是否正确处理。
复杂度分析
- 时间复杂度:
O(n)到O(输出长度)。每个输入字符都会被遍历一次,字符串重复时还需要生成最终结果。 - 空间复杂度:
O(n)。栈中保存嵌套括号之前的字符串状态,最终结果本身也需要空间。
总结
这道题的核心是“括号嵌套 = 保存现场”。
遇到 [ 时,把上一层的字符串和重复次数保存起来,然后开始处理新的一层;遇到 ] 时,说明当前层结束,把当前层结果重复指定次数后合并回上一层。
写这题时重点检查两件事:第一,数字要支持多位数;第二,遇到 [ 后一定要重置 cur 和 num。只要这两个状态处理清楚,嵌套结构就不会乱。

1430

被折叠的 条评论
为什么被折叠?



