394. 字符串解码
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: 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:辅助栈法
算法思路:
本题难点在于括号内嵌套括号,需要从内向外生成与拼接字符串,这与栈的先入后出特性对应。
构建辅助栈 stack ,遍历字符串 s 中的每个字符 c ;
当 c 为数字时,将数字字符转化为数字 multi ,用于后序倍数计算;当 c 为字母时,在 res 尾部添加 c ;当 c 为 [ 时,将当前 multi 和 res 入栈,并分别置空置 0:
记录此 [ 前临时结果 res 至栈,用于发现对应 ] 后的拼接操作;记录此 [ 前的倍数 multi 至栈,用于发现对应 ] 后,获取 multi x [...] 字符串。进入到新 [ 后, res 和 multi 重新记录。 当 c 为 ] 时, stack 出栈,拼接字符串 res = last_res + cur_multi * res ,其中:
last_res 是上个 [ 到当前 [ 的字符串,例如: "3[a2[c]]" 中的 a ;cur_multi 是当前 [ 到 ] 内字符串的重复倍数,例如: "3[a2[c]]" 中的 2 。 返回字符串 res 。
参考代码1:
class Solution {
public String
decodeString(String s
) {
StringBuilder res
= new StringBuilder();
int multi
= 0;
Stack
<Integer> stack_multi
= new Stack<>();
Stack
<String> stack_res
= new Stack<>();
for (char c
: s
.toCharArray()) {
if (c
== '[') {
stack_multi
.add(multi
);
stack_res
.add(res
.toString());
multi
= 0;
res
= new StringBuilder();
} else if (c
== ']') {
String last_res
= stack_res
.pop();
Integer cur_multi
= stack_multi
.pop();
StringBuilder temp
= new StringBuilder();
for (int i
= 0; i
< cur_multi
; i
++) {
temp
.append(res
.toString());
}
res
= new StringBuilder(last_res
+ temp
);
} else if (c
>= '0' && c
<= '9') {
multi
= multi
* 10 + Integer
.parseInt(c
+ "");
} else {
res
.append(c
);
}
}
return res
.toString();
}
}
复杂度分析:
时间复杂度
O
(
N
)
O(N)
O(N),一次遍历 s。空间复杂度
O
(
N
)
O(N)
O(N),辅助栈在极端情况下需要线性空间,例如: 2[2[2[a]]] 。
方法2:递归法
算法思路:
总体思路与辅助栈法一致,不同点在于将 [ 和 ] 分别作为递归的开启与终止条件:
当 s[i] == ']' 时,返回当前括号内记录的 res 字符串与 ] 的索引 i (更新上层递归指针位置);当 s[i] == '[' 时,开启新一层递归,记录 [...] 内字符串 tmp 和递归后的最新索引 i , 并执行 res + multi * tmp 拼接字符串。
参考代码2:
class Solution {
public String
decodeString(String s
) {
return dfs(s
, 0)[0];
}
private String
[] dfs(String s
, int i
) {
StringBuilder res
= new StringBuilder();
int multi
= 0;
while (i
< s
.length()) {
if (s
.charAt(i
) >= '0' && s
.charAt(i
) <= '9') {
multi
= multi
* 10 + Integer
.parseInt(s
.charAt(i
) + "");
} else if (s
.charAt(i
) == '[') {
String
[] tmp
= dfs(s
, i
+1);
i
= Integer
.parseInt(tmp
[0]);
while (multi
> 0) {
res
.append(tmp
[1]);
multi
--;
}
} else if (s
.charAt(i
) == ']') {
return new String[] {String
.valueOf(i
), res
.toString()};
} else {
res
.append(s
.charAt(i
));
}
i
++;
}
return new String[] {res
.toString()};
}
}
复杂度分析:
时间复杂度
O
(
N
)
O(N)
O(N),递归会更新索引,实际上还是一次遍历 s。空间复杂度
O
(
N
)
O(N)
O(N),极端情况下递归深度将会达到线性级别 。
以上谢谢大家,求赞求赞求赞!
💗 大佬们随手关注下我的wx公众号【一角钱小助手】和 掘金专栏【一角钱】 更多题解干货等你来~~