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

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

相关推荐
blammmp26 分钟前
算法专题十九:记忆化搜索(暴搜->记忆化搜索)
算法·深度优先·记忆化搜索
MicroTech20251 小时前
边缘智能的创新:MLGO微算法科技推出基于QoS感知的边缘大模型自适应拆分推理编排技术
科技·算法·ai
王哈哈^_^3 小时前
【数据集】【YOLO】目标检测游泳数据集 4481 张,溺水数据集,YOLO河道、海滩游泳识别算法实战训练教程。
人工智能·算法·yolo·目标检测·计算机视觉·分类·视觉检测
巴里巴气3 小时前
第73题 矩阵置零
线性代数·算法·矩阵
007php0073 小时前
某游戏大厂 Java 面试题深度解析(四)
java·开发语言·python·面试·职场和发展·golang·php
voice6703 小时前
密码学实验二
算法·密码学·哈希算法
Blossom.1184 小时前
把AI“编”进草垫:1KB决策树让宠物垫自己报「如厕记录」
java·人工智能·python·算法·决策树·机器学习·宠物
寂静山林4 小时前
UVa 10989 Bomb Divide and Conquer
算法
兮山与5 小时前
算法23.0
算法
共享家95275 小时前
数独系列算法
算法·深度优先