AlgoMooc
你已开通华为OD训练营权益,还差最后一步——完成入营激活(兑换课程 + 加飞书 + 登记服务群),即可解锁全部课程与专属服务。去激活 →
← OD 20 天课程

华为 OD 训练营 · 题解精讲

LC394. 字符串解码

LC394. 字符串解码 预习和复习可以查看我的讲解视频。

题目描述

给定一个经过编码的字符串,返回它解码后的字符串。 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。 示例 1: 输入:s = "3[a]2[bc]" 输出:"aaabcbc" 示例 2: 输入:s = "3[a2[c]]" 输出:"accaccacc" 示例 3: 输入:s = "2[abc]3[cd]ef" 输出:"abcabccdcdcdef" 示例 4: 输入:s = "abc3[cd]xyz" 输出:"abccdcdcdxyz" 提示: 1 <= s.length <= 30 s 由小写英文字母、数字和方括号 '[]' 组成 s 保证是一个 有效 的输入。 s 中所有整数的取值范围为 [1, 300]

题目解析

注意示例 2 ,可以发现字符串中存在括号内有嵌套括号的情况,这个时候,只有先把内层括号解码成功,才能再去解码外层括号。 ZnKrbDcR1ow47bxsFSxccklrnpb.png

也就意味着后面访问的括号比前面访问的括号还更早的进行处理,与栈的先入后出特点对应。 所以,本题使用栈来处理。 具体操作如下: 1、构建两个栈,一个是数字栈 numStack ,在遍历编码字符串过程中记录出现的数字;一个是字符串栈 strStack ,在遍历编码字符串过程中记录出现的字符串。 2、初始化两个变量,一个是 digit ,用来记录访问到字符串之前出现的数字;一个是 res ,在访问编码字符串的过程中,把得到的结果存放到 res 中。 3、接下来,开始从头到尾访问编码字符串,在访问过程中,字符会出现 4 种情况。 U6KIb2xTooUP0wx6D6Acmyk6nyd.png

4、如果是数字,需要把字符转成整型数字,然后更新到 digit 上,代表后续的字符串需要重复 digit 次。 5、如果是字符,说明它就出现一次,可以直接存放到 res 中。 6、如果是"[" ,这个时候出现了嵌套的内层编码字符串,而外层的解码需要等待内层解码的结果,那么之前已经扫描的字符需要存放起来,等到内层解码之后再重新使用。 FFUObB10SootpJxfTIKcP85En2b.png

7、如果是"]" ,此时,内层解码已经有结果,需要把它和前面的字符串进行拼接。 8、拼接的方式就是先通过 numsStack 的栈顶元素获取重复的次数,再通过 strStack 的栈顶元素获取前面的字符串。 9、最后返回 res 就行。

参考代码

参考代码

class Solution:
    def decodeString(self, s: str) -> str:
        # 创建数字栈,在遍历编码字符串过程中记录出现的数字
        numStack = []

        # 创建字符栈,在遍历编码字符串过程中记录出现的字符串
        strStack = []

        # 在访问编码字符串的过程中,用来记录访问到字符串之前出现的数字,一开始为 0
        digit = 0

        # 在访问编码字符串的过程中,把得到的结果存放到 res 中
        res = ""

        # 从头到尾遍历编码字符串
        for ch in s:

            # 在遍历过程中,字符会出现 4 种情况
            # 先获取此时访问的字符
            # 1、如果是数字,需要把字符转成整型数字
            # 注意数字不一定是 1 位,有可能是多位
            # 比如  123a,在字母 a 的前面出现了 123,表示 a 重复出现 123 次
            # 那么一开始 ch 为 1,当访问到 ch 为 2 的时候,1 和 2 要组成数字 12
            # 再出现 3 ,12 和 3 组成数字 123
            if '0' <= ch <= '9':

                # 先将字符转成整型数字 ch-‘0’
                # 补充知识:将字符'0'-'9'转换为数字,只需将字符变量减去'0'就行
                # 因为字符和数字在内存里都是以 ASCII 码形式存储的
                # 减去 '0' ,其实就是减去字符 '0' 的 ASCII 码,而字符 '0' 的 ASCII 码是 30
                # 所以减去'0'也就是减去30,然后就可以得到字符对应的数字了。
                num = int(ch)

                # 再将这个数字和前面存储的数字进行组合
                # 1 和 2 组成数字 12,也就是 1 * 10 + 2 = 12
                # 12 和 3 组成数字 123,也就是 12 * 10 + 3 = 123
                digit = digit * 10 + num 

            # 2、如果是字符
            elif ch >= 'a' and ch <= 'z':

                # 说明它就出现一次,可以直接存放到 res 中
                res += ch

            # 3、如果是"[" 
            # 出现了嵌套的内层编码字符串,而外层的解码需要等待内层解码的结果
            # 那么之前已经扫描的字符需要存放起来,等到内层解码之后再重新使用
            elif ch == '[' :

                # 把数字存放到数字栈
                numStack.append(digit)

                # 把外层的解码字符串存放到字符串栈
                strStack.append(res)

                # 开始新的一轮解码
                # 于是,digit 归零
                digit = 0

                # res 重新初始化
                res = ""

            # 4、如果是"]" 
            elif ch == ']':

                # 此时,内层解码已经有结果,需要把它和前面的字符串进行拼接

                # 第一步,先去查看内层解码的字符串需要被重复输出几次
                # 比如 e3[abc],比如内层解码结果 abc 需要输出 3 次
                # 通过数字栈提取出次数
                count = numStack.pop()

                # 第二步,通过字符串栈提取出之前的解码字符串
                outString = strStack.pop()

                # 第三步,不断的把内层解码的字符串拼接起来
                for j in range(0,count) : 
                    # 拼接到 outString 后面,拼接 count 次
                    outString += res
            

                # 再把此时得到的结果赋值给 res
                res = outString

        # 返回解码成功的字符串
        return res
四、复杂度分析
时间复杂度 O(N),一次遍历 s
空间复杂度 O(N),辅助栈在极端情况下需要线性空间。