字符串解码 图解题解
括号嵌套几层就要重复几层,从外往里替换根本下不了手——两个栈帮你记住每一层的上下文,钻进去再原路合并回来。
像打开层层嵌套的礼品盒:每遇到一个新盒子(左括号),先把手里已经拼好的东西和这层要重复的次数「存档」压入两个栈,腾空双手去拆里面的盒子;遇到右括号就把最里层的内容重复指定次数,再把存档取出来拼在后面。越深的括号越后处理,拆完再往外合并,栈的后进先出天然对应括号嵌套顺序。
这道题到底在问什么
- 输入 s
- "3[a2[c]]"
- 输出
- "accaccacc"
最优解:一步一步想明白
- 3想从外往里一次替换 3[...] 替不动,因为里面还没解完。必须先存住外层、钻进内层。
- 4用两个栈:数字栈存这层要重复几次,字符串栈存进括号前已经攒好的前缀。
- 5cur="" num归零 两栈各存一层读到左括号,先别拼字符串:把次数 3 和当前前缀压两个栈,再清空 cur 去读里面。
- 6cur="a" 两栈不动字母 a 不是括号,直接接到当前串后面。这一步两个栈都不动。
- 7cur="" num归零 栈又压一层又遇左括号:把次数 2 和此刻前缀 a 压栈,cur 清空。栈越压越深,对应括号越嵌越里。
- 8cur="c" 两栈不动在最里层读到 c,接到当前串上。注意 cur 现在只装着里层攒的 c。
- 9弹出 k=2、prev="a" cur 仍是 "c"右括号要拆成两帧看。这一帧只做弹栈:取回最近存的次数 2 和前缀 a。最近进的括号最先恢复,正是栈。
- 10cur = prev + cur×k = "a"+"cc" = "acc"这一帧合成:前缀 a 接上 c 重复两次,cur 变成 acc。弹栈和拼接拆开两帧,动作才看得清。
- 11cur = "" + "acc"×3 = "accaccacc"最后的外层右括号:弹出次数 3,把 acc 重复三次。栈空了,cur 就是最终答案。
- 12右括号 = 先弹栈拿 prev/k,再 cur=prev+cur×k这就是全题的核心动作,慢放一遍:每遇右括号,弹出最近的次数和前缀,再用 prev 加 cur 重复 k 次。
- 15左括号按下暂停键把现场压栈,右括号松开暂停键弹栈把内层结果接回去。
- 17错写 cur×k + prev:"a"+"cc" 变成 "cca"用同一组数据演一遍坑:拼接顺序写反,acc 就变成 cca,往上层一路传错。前缀永远放前面。
- 25学完别乱跳,去 栈专题 连刷同类的嵌套结构题;卡住时用 AI 答疑追问『右括号这一步为什么要弹栈』,比死背更稳。
⚠️ 容易写错的地方
✗ 错:多位数字只读一位
✓ 对:num = num×10 + digit
遇到 12[a] 会只取到 1 或 2,必须连续累乘把整段数字读完。
✗ 错:左括号后忘了重置 cur 和 num
✓ 对:压栈后立刻 cur=""、num=0
内层要从空串、零次数重新攒,不清空会把外层的串带进内层。
✗ 错:右括号写成 cur×k + prev
✓ 对:prev + cur×k
前缀必须在重复串前面。a2[c] 应得 acc,写反会得 cca。
完整代码(Python / C++ / Java)
Python
class Solution:
def decodeString(self, s):
nums, strs = [], []
cur, num = '', 0
for ch in s:
if ch.isdigit():
num = num * 10 + int(ch)
elif ch == '[':
nums.append(num)
strs.append(cur)
cur, num = '', 0
elif ch == ']':
k = nums.pop()
prev = strs.pop()
cur = prev + cur * k
else:
cur += ch
return curC++
class Solution {
public:
string decodeString(string s) {
vector<int> nums;
vector<string> strs;
string cur;
int num = 0;
for (char ch : s) {
if (isdigit(ch)) num = num * 10 + (ch - '0');
else if (ch == '[') {
nums.push_back(num);
strs.push_back(cur);
cur = ""; num = 0;
} else if (ch == ']') {
int k = nums.back(); nums.pop_back();
string prev = strs.back(); strs.pop_back();
string rep; while (k--) rep += cur;
cur = prev + rep;
} else cur += ch;
}
return cur;
}
};Java
class Solution {
public String decodeString(String s) {
Deque<Integer> nums = new ArrayDeque<>();
Deque<String> strs = new ArrayDeque<>();
StringBuilder cur = new StringBuilder();
int num = 0;
for (char ch : s.toCharArray()) {
if (Character.isDigit(ch)) num = num * 10 + (ch - '0');
else if (ch == '[') {
nums.push(num);
strs.push(cur.toString());
cur = new StringBuilder(); num = 0;
} else if (ch == ']') {
int k = nums.pop();
String prev = strs.pop();
StringBuilder sb = new StringBuilder(prev);
while (k-- > 0) sb.append(cur);
cur = sb;
} else cur.append(ch);
}
return cur.toString();
}
}套路模板 · 嵌套结构「压栈保现场 → 弹栈合现场」
# 嵌套/括号题判据:遇到『开始符』要不要存现场?遇到『结束符』要不要弹栈合并?
num_stack, str_stack = [], []
cur, num = '', 0
for ch in s:
if 是数字: num = num*10 + 值 # 累计本层参数
elif 是开始符: # 进内层
num_stack.append(num) # 存现场
str_stack.append(cur)
cur, num = '', 0 # 重置
elif 是结束符: # 出内层
k = num_stack.pop() # 弹现场
cur = str_stack.pop() + cur*k # 合并(前缀在前)
else: cur += ch
return curvector<int> numStk; vector<string> strStk;
string cur; int num = 0;
for (char ch : s) {
if (isdigit(ch)) num = num*10 + (ch-'0'); // 累计参数
else if (是开始符) { // 存现场
numStk.push_back(num); strStk.push_back(cur);
cur = ""; num = 0;
} else if (是结束符) { // 弹栈合并
int k = numStk.back(); numStk.pop_back();
string prev = strStk.back(); strStk.pop_back();
string rep; while (k--) rep += cur; cur = prev + rep;
} else cur += ch;
}
return cur;Deque<Integer> numStk = new ArrayDeque<>();
Deque<String> strStk = new ArrayDeque<>();
StringBuilder cur = new StringBuilder(); int num = 0;
for (char ch : s.toCharArray()) {
if (Character.isDigit(ch)) num = num*10 + (ch-'0'); // 累计参数
else if (是开始符) { // 存现场
numStk.push(num); strStk.push(cur.toString());
cur = new StringBuilder(); num = 0;
} else if (是结束符) { // 弹栈合并
int k = numStk.pop(); String prev = strStk.pop();
StringBuilder sb = new StringBuilder(prev);
while (k-- > 0) sb.append(cur); cur = sb;
} else cur.append(ch);
}
return cur.toString();复杂度
时间复杂度
O(|ans|)
每个输出字符至少被构造一次;输出多长就要拼多长
空间复杂度
O(depth + |ans|)
两栈深度=括号嵌套层数,加上结果串本身
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 字符串解码 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么要用两个栈,不能合成一个?+
次数是 int、前缀是 string,两类数据各自一栈最清晰;也可以合成一个存 (num, prevStr) 的结构体栈,本质一样。
这道题为什么用「栈」,换最直接的暴力解会差在哪?+
栈抓住了本题的结构特征,把暴力解里重复的工作省掉;暴力解通常要多嵌套一层枚举,数据一大就超时。具体对比见上文「暴力解及其卡点」与「最优解逐步推演」两节。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。