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 标签闭合检测)

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

相关推荐
北域码匠8 小时前
高通滤波算法深度解析(High-Pass Filter)
stm32·算法·c#·数字信号处理·嵌入式开发·滤波算法·高通滤波
指掀涛澜天下惊8 小时前
强化学习进阶篇八 策略梯度算法
深度学习·学习·算法·强化学习
乌萨奇也要立志学C++8 小时前
【洛谷】kmp算法
开发语言·算法
禹凕8 小时前
Dijkstra算法详解与应用
python·算法
YaraMemo9 小时前
元启发式算法框架
人工智能·算法·5g·信息与通信·启发式算法·信号处理
weixin_3077791310 小时前
一维无粘 Burgers 方程的激波形成问题:MacCormack 格式求解
c++·算法·matlab
HugoStudio_SWAN10 小时前
洛谷 B4500 / B4449 / B3843 凯撒密码、密码强度与密码合规——加密与安全的三道门
c++·学习·程序人生·算法·安全
枫叶林FYL11 小时前
【群体智能集群控制工程实践】第3章 一致性协同算法
算法
禹凕11 小时前
滑动窗口算法实战指南
python·算法
DisonTangor11 小时前
【腾讯混元雪耻归来】 Hy4 preview:770B 参数 MoE 旗舰模型,1M 上下文全面开源
人工智能·算法·开源·aigc·腾讯云·腾讯云ai代码助手