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

相關文章
相關標籤/搜索