题目
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: 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
这是我们遇到的第一个中等难度的题,不过不用担心,第一,在我们本阶段的题解中,中等难度的题大约只有这么一个;第二,中等难度的题也没有想象中的那么难,仔细分析题目和示例依然可以做出来的;第三,中等难度的题在一线大厂的面试题中几乎占据了50%以上的分量,不管有多担心,中等难度的题依然是我们需要下力气去研究和熟练的题目。
我们观察示例,可以发现,其实输入字符串可以分为最小的单位,我们这里称为元组,元组的组成的共性是什么?
数字[字符串],也就是:一个数字+左中括号“[”+字符串]+右中括号“]”
其中数字和“[”、“]”可以没有,这表示字符串无需重复,直接输出即可。而元组本身又可以成为其他元组的字符串,构成一个复合元组。
仔细分析,可以看到,每当遇到右中括号“]”,表示前面的字符串需要进行重复,重复的字符串到哪里截止呢?需要往前走,到最近的一个左中括号“[”为止。重复几遍呢?再往前走,离“[”最近的一个数字。
通过分析过程,我们是否可以得到什么提示?需要按顺序往前回溯曾经处理的数据,是不是有点先进后出的感觉。所以可以利用栈来处理这个题目。我们以实例2中的“3[a2[c]]”来演示这个过程。
首先按字符读取字符串,只要不是右中括号“]”,将数据逐一压栈;当遇到右中括号“]”,开始出栈,一直出栈到遇到的第一个左中括号“[”的前一个为止;根据出栈的元素拼凑出一个临时字符串,并将临时字符串压栈;
继续读取原字符串,重复上述的过程,遇到右中括号“]”之前压栈,遇到右中括号“]”开始出栈并拼凑重复的字符串,直到原字符串读取完毕以及栈为空为止。
但是在具体的代码实现上,需要注意很重要的一点,题目示例中的数字是个位数,但是实际上,数字完全有可能是个多位数,所以数字需要单独处理一下。同时在栈的选择上,可以使用JDK中的Stack或者LinkedList都可以。
代码
import java.util.Collections;
import java.util.LinkedList;
import java.util.Stack;
/**
* @author :
* @description :(LeetCode-394) 字符串解码
* 给定一个经过编码的字符串,返回它解码后的字符串。
* 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。
* 注意 k 保证为正整数。
* 示例 1:
* 输入:s = "3[a]2[bc]"
* 输出:"aaabcbc"
*/
public class DecodeString_394_UseList {
int ptr;
/*LeetCode官方解法*/
public String decodeString(String s) {
LinkedList<String> stk = new LinkedList<String>();
ptr = 0;
while (ptr < s.length()) {
char cur = s.charAt(ptr);
if (Character.isDigit(cur)) {
/*处理数字,使数字完整*/
String digits = getDigits(s);
stk.addLast(digits);
} else if (Character.isLetter(cur) || cur == '[') {
/*处理普通字符和“[”*/
stk.addLast(String.valueOf(s.charAt(ptr++)));
} else {
/*遇见了“]”,处理相匹配的“[”之间的字符 */
++ptr;
/*使用另一个list,将字符串进行组合*/
LinkedList<String> sub = new LinkedList<String>();
while (!"[".equals(stk.peekLast())) {
sub.addLast(stk.removeLast());
}
/*因为栈的特点,导致组合的字符串
* 和原本的字符串相比是倒序的,
* 需要反转一次*/
Collections.reverse(sub);
/* 左括号出栈*/
stk.removeLast();
/* 此时栈顶为当前 sub 对应的字符串应该出现的次数*/
int repTime = Integer.parseInt(stk.removeLast());
StringBuffer t = new StringBuffer();
String o = getString(sub);
// 构造字符串
while (repTime-- > 0) {
t.append(o);
}
/* 构造好的字符串入栈*/
stk.addLast(t.toString());
}
}
return getString(stk);
}
public String getDigits(String s) {
StringBuffer ret = new StringBuffer();
while (Character.isDigit(s.charAt(ptr))) {
ret.append(s.charAt(ptr++));
}
return ret.toString();
}
public String getString(LinkedList<String> v) {
StringBuffer ret = new StringBuffer();
for (String s : v) {
ret.append(s);
}
return ret.toString();
}
public static void main(String[] args) {
System.out.println(new DecodeString_394_UseList().decodeString("3[a2[c]]"));
}
}
import java.util.Stack;
/**
* @author :
* @description :(LeetCode-394) 字符串解码
* 基于Stack的实现
*/
public class DecodeString_394_UseStack {
public static String decodeString(String s) {
String res = "";
Stack<Integer> countStack = new Stack<>();
Stack<String> resStack = new Stack<>();
int idx = 0;
while (idx < s.length()) {
char cur = s.charAt(idx);
/*处理数字*/
if (Character.isDigit(cur)) {
StringBuffer ret = new StringBuffer();
while (Character.isDigit(s.charAt(idx))) {
ret.append(s.charAt(idx++));
}
countStack.push(Integer.parseInt(ret.toString()));
}
else if (cur == '[') {
/*处理“[”*/
resStack.push(res);
res = "";
idx++;
}
else if (cur == ']') {
/*处理“]”,处理相匹配的“[”之间的字符*/
StringBuffer temp = new StringBuffer (resStack.pop());
int repeatTimes = countStack.pop();
for (int i = 0; i < repeatTimes; i++) {
temp.append(res);
}
res = temp.toString();
idx++;
}
else {
/*处理普通字符*/
res += s.charAt(idx++);
}
}
return res;
}
public static void main(String[] args) {
System.out.println(decodeString("3[a2[c]]"));
}
}