LeetCode 394. 字符串解码(Decode String)
文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 代码讲解
- 示例测试及结果
- 输出结果:
- 时间复杂度
- 空间复杂度
- 总结
摘要
在实际开发中,我们经常需要解析字符串,比如处理模板语言(像 HTML、Markdown、或自定义语法)、实现配置文件解析、甚至在前后端数据传输中做字符串编码与解码。而这道 LeetCode 394《字符串解码》正是一个非常典型的“栈结构解析”题。
它不仅考察我们对字符串处理的熟悉程度,还能锻炼我们对嵌套结构的思维建模能力。很多开发者在刚接触时容易被多层嵌套搞晕,其实只要理解核心思想——用栈保存当前状态,一切就变得简单多了。
描述
题目要求我们把一个经过编码的字符串还原成原始字符串。
编码规则是这样的:
k[encoded_string]
其中 encoded_string
要重复 k
次。例如:
"3[a]"
→"aaa"
"2[bc]"
→"bcbc"
当然,这题的难点不是这里,而是嵌套结构!
比如:
"3[a2[c]]"
→ 里面的"2[c]"
要先被解出来成"cc"
,再重复 3 次得到"accaccacc"
。
换句话说,你需要一层层解析 []
内的内容,直到没有嵌套为止。
题目还保证输入是合法的,没有奇怪的格式,比如不会出现 "3a"
或 "2[4]"
这种不合规范的情况。
题解答案
思路其实很直观,只要你能想到“栈”这个数据结构。
我们可以用两个栈:
- 一个栈
numStack
存放数字(也就是重复次数 k) - 一个栈
strStack
存放当前字符串状态
当我们遇到 '['
,就把当前的数字和字符串状态压入栈中;
当遇到 ']'
,就弹出上一个状态,拼接出完整的字符串;
当遇到普通字母时,直接追加到当前字符串。
这个过程其实就像递归一样,只不过我们用栈来模拟递归的过程。
题解代码分析
来看完整的 Swift 实现
import Foundationclass Solution {func decodeString(_ s: String) -> String {var numStack: [Int] = [] // 保存重复次数var strStack: [String] = [] // 保存上一次的字符串状态var currentNum = 0 // 当前的数字var currentStr = "" // 当前构造的字符串for char in s {if char.isNumber {// 如果是数字,更新当前数字(可能是多位数)currentNum = currentNum * 10 + Int(String(char))!} else if char == "[" {// 遇到左括号,把当前状态压栈numStack.append(currentNum)strStack.append(currentStr)currentNum = 0currentStr = ""} else if char == "]" {// 遇到右括号,弹出栈顶数据let repeatCount = numStack.removeLast()let lastStr = strStack.removeLast()// 把当前字符串重复指定次数后接到上一个状态后面currentStr = lastStr + String(repeating: currentStr, count: repeatCount)} else {// 普通字符,直接拼接currentStr.append(char)}}return currentStr}
}
代码讲解
我们来细拆一下逻辑:
-
识别数字
当遍历到一个数字(比如"3"
)时,我们用currentNum = currentNum * 10 + ...
的方式构建完整数字,这样就能支持多位数(如"12[a]"
→ 重复 12 次)。 -
遇到
[
当前字符串和数字状态都要“入栈保存”,因为后面会进入一个新的嵌套层级。
然后重置currentStr
和currentNum
,为下一层准备。 -
遇到
]
表示当前嵌套层结束。
从栈中取出上一个状态,把当前层的结果重复指定次数后拼接上去。 -
普通字母
直接追加在当前字符串currentStr
里。
这整个流程其实就像在不断地“进入括号、退出括号”,每次退出括号就拼接一次完整字符串。
用栈保存上下文状态,正好能完美处理这种嵌套关系。
示例测试及结果
我们来跑几个典型示例:
let solution = Solution()print(solution.decodeString("3[a]2[bc]")) // 输出: aaabcbc
print(solution.decodeString("3[a2[c]]")) // 输出: accaccacc
print(solution.decodeString("2[abc]3[cd]ef")) // 输出: abcabccdcdcdef
print(solution.decodeString("abc3[cd]xyz")) // 输出: abccdcdcdxyz
输出结果:
aaabcbc
accaccacc
abcabccdcdcdef
abccdcdcdxyz
是不是很直观?
无论嵌套多少层,只要括号匹配正确,栈的结构都能帮我们把层级理得清清楚楚。
时间复杂度
这段代码整体是 O(n),其中 n 是字符串长度。
每个字符都只被遍历一次,而且入栈、出栈的操作都是 O(1)。
即使有嵌套,也只是逻辑层次不同,时间复杂度依然是线性的。
空间复杂度
空间复杂度取决于栈的深度。
在最坏情况下,比如 "3[2[2[a]]]"
,栈的深度等于括号嵌套层数。
所以空间复杂度是 O(n),但一般远小于 n,因为嵌套层级通常不会太深。
总结
这道题最大的价值在于它能帮你真正理解“用栈解决嵌套结构”的思想。
它不仅能应用在算法题上,也能帮助你在项目中处理类似的语法结构,比如:
- 解析配置语言(YAML、JSON、DSL)
- 模板引擎展开(如
{{#each}} ... {{/each}}
) - 富文本编辑器的格式匹配(HTML 标签闭合检测)
如果你能熟练掌握这种“栈 + 状态保存 + 拼接回溯”的思路,那么很多复杂的字符串题都会迎刃而解。