LeetCode每日一题——394. 字符串解码

*题目描述

给定一个通过编码的字符串,返回它解码后的字符串。python

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。git

你能够认为输入字符串老是有效的;输入字符串中没有额外的空格,且输入的方括号老是符合格式要求的。算法

此外,你能够认为原始数据不包含数字,全部的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。bash

示例:app

s = "3[a]2[bc]", 返回 "aaabcbc". s = "3[a2[c]]", 返回 "accaccacc". s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".函数

*本菜鸡的解法

*思路

看到题目想了好一下子,才有点眉目,看到题目的标签中有“栈”,“深度优先搜索”这两个标签,总以为不须要用递归,但实际上最后仍是用了递归。ui

心路历程以下(包含错误心路历程):编码

  • 首先发现字符串先后可能出现不重复的字母,如:“ab3[cd]e”,因而思考首先将字符串先后的单独字母剔除,分别存为first和end,而后将中间的部分做为middle去单独处理,即first=“ab”,middle=“3[cd]”,end="e"
  • 对于中间涉及循环的部分,从前到后遍历,对于每遇到的一个“[”,都寻找其最大的一个重复部分,即对于“3[ab4[c]]”而言,第一个且最大的重复部分是“[ab4[c]]”,若是是“3[ab]4[c]”的话,第一个且最大的循环就是“[ab]”
  • 对每一个部分重复其前面的重复次数,而后和first,end拼接起来返回
  • 在考虑循环以前应考察字符串中是否存在“[”,没有“[”则说明没有循环,直接返回便可
  • 当出现循环嵌套循环的时候就可使用递归

问题出如今第二点上,当我把先后的不重复字母都删掉以后,认为内层的循环部分不会再出现有不循环的字母了,所以对于middle层的处理逻辑是遍历所有字符串,找到循环体以后就循环其前面的数字次,这个数字怎么定位呢?一开始由于咱们已经去除了first和end,所以第一个循环的数字确定是从第零位开始的,到“[”前为止,到这里都没问题,可是等到第二个循环体的时候,因为我默认不会再有不循环的字母,所以第二个循环体的循环次数我是将上一个循环体的结尾“]”第二个循环体的“[” 中间的这一部分强转为int类型做为循环次数的,而这显然就出现了问题,好比:spa

对于“3[z]2[2[y]pq4[2[jk]e1[f]]]ef”,首先我去除“ef”,剩下3[z]2[2[y]pq4[2[jk]e1[f]]],而后我循环3次z,到了第二个大循环体,循环2次[2[y]pq4[2[jk]e1[f]]],对于这个循环体里面,首先循环2次y,而后个人程序准备循环 “pq4” 次[2[jk]e1[f]],这明显就报错了。指针

后面我发现,个人单个函数的递纳入口处已经判断并提取过了子串的first和end,那么我为啥不直接把后面的子串直接带入递归循环呢?因而我就把每次找到的第一个最大循环体和后面余下的子串分别放入递归中,成功AC。

*代码

1    class Solution: 
2        def decodeString(self, s: str) -> str: 
3            if '[' not in s: 
4                return s 
5            else: 
6                a_index=0 
7                b_index=len(s)-1 
8                while s[a_index].isalpha(): 
9                    a_index+=1 
10               first=s[:a_index] 
11               while s[b_index].isalpha(): 
12                   b_index-=1 
13               end=s[b_index+1:] 
14               middle=s[a_index:b_index+1] 
15               mid='' 
16               left_stack=[] 
17               for i in range(len(middle)): 
18                   if middle[i] == '[': 
19                       left_stack.append(i) 
20                       continue 
21                   if middle[i] == ']': 
22                       a = left_stack.pop(-1) 
23                       if len(left_stack) == 0: 
24                           mid += int(middle[0:a]) * self.decodeString(middle[a + 1:i]) 
25                           mid+=self.decodeString(middle[i+1:]) 
26                           break 
27               return first + mid + end
复制代码

一、首先判断串中是否含有“[”,若是不含说明没循环,直接返回,若是有则下一步
二、6-14行经过分别从前向后和从后向前的判断字符,找到了字符串中的first和end和middle部分,其中first和end都是直接的字符串,能够直接拼接,middle部分须要循环
三、借助一个栈找到middle中第一个且最大的循环,而后将这部分和剩下的部分分别送入第一步,进行递归

*举例

以字符串“2[2[y]pq4[jk]]3[z]ef”为例,解释一下程序思路。

一、判断有“[”,开始寻找first,middle,end
二、first='',middle='2[2[y]pq4[jk]]3[z]',end='ef'
三、middle中第一个且最大的一个循环是'2[2[y]pq4[jk]]',剩余部分是'3[z]',分别继续判断
四、对于3中的第一个循环,分解得first='',middle='2[y]pq4[jk]',end=''
五、middle中第一个且最大的一个循环是'2[y]',剩余部分是'pq4[jk]',分别继续判断
六、第一个中first='',middle='2[y]',end='',其中middle的'y'在下一个递归中没有'[',所以直接返回,故这里是返回'yy'
七、剩余部分的first='pq',middle='4[jk]',end='',其中middle部分在下一个递归中没有'[',所以直接返回,故这里是返回'pqjkjkjkjk'
八、结合六、7两步的结果,对于4获得的结果是'yypqjkjkjkjk',返回的结果是'yypqjkjkjkjkyypqjkjkjkjk'
九、对于3中的剩余部分,其first='',middle='3[z]',end='',其中middle部分在下一个递归中没有'[',所以直接返回,故这里是返回'zzz'
十、3获得的结果是'yypqjkjkjkjkyypqjkjkjkjkzzz'
十一、结合2中的first和end,获得最后的输出'yypqjkjkjkjkyypqjkjkjkjkzzzef'

*分析

能够发现个人算法中实际上从前到后遍历了不止一遍字符串,在每次判断middle的过程当中,先遍历了一部分找到middle中第一个最大的循环体,而后这一部分在下一层递归中又经历了first和end的遍历,因此在时间复杂度上比较高,在最差的状况下应该是会达到O(N!)级别,例如:2[3[4[5[6[7[8[9[a]b]c]d]e]f]g]h]i

而在空间复杂度上因为递归传递的数据太多,致使空间复杂度也很高,因此这个算法实际上不好,基本上只是能完成需求的级别,最后的结果就是

执行结果:
经过

执行用时 :48 ms, 在全部 python3 提交中击败了55.68%的用户

内存消耗 :13.9 MB, 在全部 python3 提交中击败了5.26%的用户
复制代码

唉仍是菜

*大佬的解法

本身的算法解完了发现不成,仍是得看大佬的解法,这里直接贴大佬的解法连接,并附上本身按照大佬解法写出的代码

**连接

leetcode-cn.com/problems/de…

**代码

1    class Solution: 
2        def decodeString(self, s: str) -> str: 
3            def dfs(s, index, length): 
4                multi = 0 
5                res = '' 
6                while index!=length: 
7                # for i in range(index, length): 
8                    if s[index].isalpha(): 
9                        res += s[index] 
10                   elif s[index].isdigit(): 
11                       multi = multi * 10 + int(s[index]) 
12                   elif s[index] == '[': 
13                       r=dfs(s, index + 1, length) 
14                       res += multi*r[0] 
15                       index=r[1] 
16                       multi=0 
17                   elif s[index] == ']': 
18                       return res,index 
19                   index+=1 
20               return res 
21           return dfs(s,0,len(s))
复制代码

**分析

这样代码从前到后只须要遍历一遍便可,时间复杂度O(N),空间复杂度O(N)

这个代码是看了大佬的解法后本身理解一遍后从新写的,其中感到最trick的地方就是重复次数的读取方式,也就是multi,将其初始化为0,而后若是读到了数字,就将其自己乘10而后和当前数字相加,这恰好可以知足任意位数的数字,并且有多少顺序遍历过去就能够了,不像我以前的想法是先找到是数字的部分,而后总体一个int()强转,这太费时间了,大佬不亏是大佬。用了大佬的思路后,时间真是蹭蹭的减小呀

另外的一个trick的地方应该是python的缘故,能够在方法中返回不一样类型的结果,像这里遇到']'的时候,返回了字符串和'['的指针,而在最后所有遍历完以后,返回的倒是字符串,这里python不须要像Java同样定义一个专门的返回结构,因此很方便

执行结果:经过

执行用时 :24 ms, 在全部 python3 提交中击败了99.89%的用户

内存消耗 :13.8 MB, 在全部 python3 提交中击败了5.26%的用户
复制代码

*End

相关文章
相关标签/搜索