LeetCode 394. 字符串解码(Decode String)


文章目录

摘要

在实际开发中,我们经常需要解析字符串,比如处理模板语言(像 HTML、Markdown、或自定义语法)、实现配置文件解析、甚至在前后端数据传输中做字符串编码与解码。而这道 LeetCode 394《字符串解码》正是一个非常典型的"栈结构解析"题。

它不仅考察我们对字符串处理的熟悉程度,还能锻炼我们对嵌套结构 的思维建模能力。很多开发者在刚接触时容易被多层嵌套搞晕,其实只要理解核心思想------用栈保存当前状态,一切就变得简单多了。

描述

题目要求我们把一个经过编码的字符串还原成原始字符串。

编码规则是这样的:

txt 复制代码
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 实现

swift 复制代码
import Foundation

class 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 = 0
                currentStr = ""
            } else if char == "]" {
                // 遇到右括号,弹出栈顶数据
                let repeatCount = numStack.removeLast()
                let lastStr = strStack.removeLast()
                // 把当前字符串重复指定次数后接到上一个状态后面
                currentStr = lastStr + String(repeating: currentStr, count: repeatCount)
            } else {
                // 普通字符,直接拼接
                currentStr.append(char)
            }
        }
        
        return currentStr
    }
}

代码讲解

我们来细拆一下逻辑:

  1. 识别数字

    当遍历到一个数字(比如 "3")时,我们用 currentNum = currentNum * 10 + ... 的方式构建完整数字,这样就能支持多位数(如 "12[a]" → 重复 12 次)。

  2. 遇到 [

    当前字符串和数字状态都要"入栈保存",因为后面会进入一个新的嵌套层级。

    然后重置 currentStrcurrentNum,为下一层准备。

  3. 遇到 ]

    表示当前嵌套层结束。

    从栈中取出上一个状态,把当前层的结果重复指定次数后拼接上去。

  4. 普通字母

    直接追加在当前字符串 currentStr 里。

这整个流程其实就像在不断地"进入括号、退出括号",每次退出括号就拼接一次完整字符串。

用栈保存上下文状态,正好能完美处理这种嵌套关系。

示例测试及结果

我们来跑几个典型示例:

swift 复制代码
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

输出结果:

txt 复制代码
aaabcbc
accaccacc
abcabccdcdcdef
abccdcdcdxyz

是不是很直观?

无论嵌套多少层,只要括号匹配正确,栈的结构都能帮我们把层级理得清清楚楚。

时间复杂度

这段代码整体是 O(n) ,其中 n 是字符串长度。

每个字符都只被遍历一次,而且入栈、出栈的操作都是 O(1)。

即使有嵌套,也只是逻辑层次不同,时间复杂度依然是线性的。

空间复杂度

空间复杂度取决于栈的深度。

在最坏情况下,比如 "3[2[2[a]]]",栈的深度等于括号嵌套层数。

所以空间复杂度是 O(n),但一般远小于 n,因为嵌套层级通常不会太深。

总结

这道题最大的价值在于它能帮你真正理解"用栈解决嵌套结构"的思想。

它不仅能应用在算法题上,也能帮助你在项目中处理类似的语法结构,比如:

  • 解析配置语言(YAML、JSON、DSL)
  • 模板引擎展开(如 {``{#each}} ... {``{/each}}
  • 富文本编辑器的格式匹配(HTML 标签闭合检测)

如果你能熟练掌握这种"栈 + 状态保存 + 拼接回溯"的思路,那么很多复杂的字符串题都会迎刃而解。

相关推荐
tt5555555555553 小时前
LeetCode进阶算法题解详解
算法·leetcode·职场和发展
让我们一起加油好吗3 小时前
【基础算法】DFS中的剪枝与优化
算法·深度优先·剪枝
Q741_1474 小时前
C++ 模拟题 力扣495. 提莫攻击 题解 每日一题
c++·算法·leetcode·模拟
我命由我123454 小时前
Excel - Excel 列出一列中所有不重复数据
经验分享·学习·职场和发展·word·powerpoint·excel·职场发展
Felven5 小时前
A. Be Positive
算法
小O的算法实验室5 小时前
2026年COR SCI2区,自适应K-means和强化学习RL算法+有效疫苗分配问题,深度解析+性能实测,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
青岛少儿编程-王老师5 小时前
CCF编程能力等级认证GESP—C++7级—20250927
数据结构·c++·算法
夏鹏今天学习了吗6 小时前
【LeetCode热题100(39/100)】对称二叉树
算法·leetcode·职场和发展
天选之女wow6 小时前
【代码随想录算法训练营——Day34】动态规划——416.分割等和子集
算法·leetcode·动态规划