Skip to content

Commit 73a7504

Browse files
committed
Create 394.decode-string.md
1 parent fcc9e0b commit 73a7504

File tree

1 file changed

+253
-0
lines changed

1 file changed

+253
-0
lines changed

problems/394.decode-string.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
## 题目地址(394. 字符串解码)
2+
3+
https://leetcode-cn.com/problems/decode-string/
4+
5+
## 题目描述
6+
7+
```
8+
给定一个经过编码的字符串,返回它解码后的字符串。
9+
10+
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
11+
12+
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
13+
14+
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
15+
16+
 
17+
18+
示例 1:
19+
20+
输入:s = "3[a]2[bc]"
21+
输出:"aaabcbc"
22+
示例 2:
23+
24+
输入:s = "3[a2[c]]"
25+
输出:"accaccacc"
26+
示例 3:
27+
28+
输入:s = "2[abc]3[cd]ef"
29+
输出:"abcabccdcdcdef"
30+
示例 4:
31+
32+
输入:s = "abc3[cd]xyz"
33+
输出:"abccdcdcdxyz"
34+
35+
```
36+
37+
## 前置知识
38+
39+
-
40+
- 括号匹配
41+
42+
## 使用栈
43+
44+
### 思路
45+
46+
题目要求将一个经过编码的字符解码并返回解码后的字符串。题目给定的条件是只有四种可能出现的字符
47+
48+
1. 字母
49+
2. 数字
50+
3. [
51+
4. ]
52+
53+
并且输入的方括号总是满足要求的(成对出现),数字只表示重复次数。
54+
55+
那么根据以上条件,可以看出其括号符合栈先进后出的特性以及递归的特质,稍后我们使用递归来解。
56+
57+
那么现在看一下迭代的解法。
58+
59+
我们可以利用 stack 来实现这个操作,遍历这个字符串 s,判断每一个字符的类型:
60+
61+
- 如果是字母 --> 添加到 stack 当中
62+
- 如果是数字 --> 先不着急添加到 stack 中 --> 因为有可能有多位
63+
- 如果是 [ --> 说明重复字符串开始 --> 将数字入栈 --> 并且将数字清零
64+
- 如果是 ] --> 说明重复字符串结束 --> 将重复字符串重复前一步储存的数字遍
65+
66+
拿题目给的例子`s = "3[a2[c]]"` 来说:
67+
68+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfghoy69l3j30ga03g3yq.jpg)
69+
70+
在遇到 `` 之前,我们不断执行压栈操作:
71+
72+
![image](https://user-images.githubusercontent.com/12479470/83752461-2a6ab780-a69b-11ea-90fa-55da20fc5c35.png)
73+
74+
当遇到 ``的时候,说明我们应该出栈了,不断出栈知道对应的``,这中间的就是 repeatStr。
75+
76+
![image](https://user-images.githubusercontent.com/12479470/83752523-41a9a500-a69b-11ea-83fa-39640dc4105d.png)
77+
78+
但是要重复几次呢? 我们需要继续出栈,直到非数字为止,这个数字我们记录为 repeatCount。
79+
80+
![image](https://user-images.githubusercontent.com/12479470/83752775-aa911d00-a69b-11ea-8217-52931ca0f646.png)
81+
82+
而最终的字符串就是 repeatCount 个 repeatStr 拼接的形式。 **并将其看成一个字母压入栈中**
83+
84+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfghxjk5ejj310g0dt41r.jpg)
85+
86+
继续,后面的逻辑是一样的:
87+
88+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfgi1jhwb3j30uv09q0vd.jpg)
89+
90+
(最终图)
91+
92+
### 代码
93+
94+
代码支持:Python
95+
96+
Python:
97+
98+
```py
99+
class Solution:
100+
def decodeString(self, s: str) -> str:
101+
stack = []
102+
for c in s:
103+
if c == ']':
104+
repeatStr = ''
105+
repeatCount = ''
106+
while stack and stack[-1] != '[':
107+
repeatStr = stack.pop() + repeatStr
108+
# pop 掉 "["
109+
stack.pop()
110+
while stack and stack[-1].isnumeric():
111+
repeatCount = stack.pop() + repeatCount
112+
stack.append(repeatStr * int(repeatCount))
113+
else:
114+
stack.append(c)
115+
return "".join(stack)
116+
```
117+
118+
**复杂度分析**
119+
120+
- 时间复杂度:$O(N)$,其中 N 为解码后的 s 的长度。
121+
- 空间复杂度:$O(N)$,其中 N 为解码后的 s 的长度。
122+
123+
## 递归
124+
125+
### 思路
126+
127+
递归的解法也是类似。由于递归的解法并不比迭代书写简单,以及递归我们将在第三节讲述。
128+
129+
主逻辑仍然和迭代一样。 只不过每次碰到左括号就进入递归,碰到右括号就跳出递归返回即可。
130+
131+
唯一需要注意的是,我这里使用了 start 指针跟踪当前遍历到的位置, 因此如果使用递归需要在递归返回后更新指针。
132+
133+
### 代码
134+
135+
代码支持: Python3, Go, PHP
136+
137+
Python3 Code:
138+
139+
```py
140+
class Solution:
141+
142+
def decodeString(self, s: str) -> str:
143+
def dfs(start):
144+
repeat_str = repeat_count = ''
145+
while start < len(s):
146+
if s[start].isnumeric():
147+
repeat_count += s[start]
148+
elif s[start] == '[':
149+
# 更新指针
150+
start, t_str = dfs(start + 1)
151+
# repeat_count 仅作用于 t_str,而不作用于当前的 repeat_str
152+
repeat_str = repeat_str + t_str * int(repeat_count)
153+
repeat_count = ''
154+
elif s[start] == ']':
155+
return start, repeat_str
156+
else:
157+
repeat_str += s[start]
158+
start += 1
159+
return repeat_str
160+
return dfs(0)
161+
```
162+
163+
Go Code:
164+
165+
```go
166+
func decodeString(s string) string {
167+
var stack []byte // 对比 string 类型, 内存 2.2M -> 2.0M
168+
var b byte
169+
var n int
170+
for _, c := range s {
171+
b = byte(c)
172+
if b == ']' {
173+
var repeatStr, repeatCnt []byte
174+
n = len(stack)
175+
for n > 0 && stack[n-1] != '[' {
176+
b, stack = stack[n-1], stack[:n-1]
177+
repeatStr = append([]byte{b}, repeatStr...) // 注意顺序
178+
n--
179+
}
180+
stack = stack[:n-1] // 去掉 '['
181+
n--
182+
for n > 0 && isDigit(stack[n-1]) {
183+
b, stack = stack[n-1], stack[:n-1]
184+
repeatCnt = append([]byte{b}, repeatCnt...)
185+
n--
186+
}
187+
k, _ := strconv.Atoi(string(repeatCnt))
188+
stack = append(stack, repeatByte(k, repeatStr)...)
189+
} else {
190+
stack = append(stack, b)
191+
}
192+
}
193+
return string(stack)
194+
}
195+
196+
func isDigit(b byte) bool {
197+
if b >= 48 && b <= 57 {
198+
return true
199+
}
200+
return false
201+
}
202+
203+
func repeatByte(k int, b []byte) []byte {
204+
var ans []byte
205+
for i := 0; i < k; i++ {
206+
ans = append(ans, b...)
207+
}
208+
return ans
209+
}
210+
```
211+
212+
PHP Code:
213+
214+
```php
215+
class Solution {
216+
217+
/**
218+
* @param String $s
219+
* @return String
220+
*/
221+
function decodeString($s) {
222+
$stack = [];
223+
for ($i=0; $i<strlen($s); $i++) {
224+
if ($s[$i]==']') {
225+
$repeatStr = $repeatCnt = '';
226+
while ($stack && end($stack) != '[') { // 注意: PHP 中数组最后一个元素 end($stack)
227+
$repeatStr = array_pop($stack) . $repeatStr;
228+
$t = end($stack);
229+
}
230+
array_pop($stack); // 去掉 '['
231+
while ($stack && is_numeric(end($stack))) {
232+
$repeatCnt = array_pop($stack) . $repeatCnt;
233+
}
234+
array_push($stack, str_repeat($repeatStr, $repeatCnt));
235+
} else {
236+
array_push($stack, $s[$i]);
237+
}
238+
}
239+
return join('', $stack);
240+
}
241+
}
242+
```
243+
244+
**复杂度分析**
245+
246+
- 时间复杂度:$O(N)$,其中 N 为解码后的 s 的长度。
247+
- 空间复杂度:$O(N)$,其中 N 为解码后的 s 的长度。
248+
249+
更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 目前已经 37K star 啦。
250+
251+
大家也可以关注我的公众号《力扣加加》获取更多更新鲜的 LeetCode 题解
252+
253+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)