自己想到的方式就是递归和栈两种
栈
因为要获得一对"[]"中间的字符串内容,用stack1来保存遍历了的字符序列(stack1的类型是Stack<Character>)每遇到']'就从stack1中pop出元素直到遇到'[',就能获得对应的一对"[]"及其间的元素(元素序列是倒序,所以要用sb.insert(),不能用sb.append()),再pop一个元素就是其间元素应该重复的次数,按次数重复后就得到decoded substring;
将decoded substring放入另一个栈stack2保存(stack2的类型是Stack<String>),向stack1中push一个字符'-'表示此处有一个decoded substring,然后继续向后遍历,直到整个字符串被遍历完;
最后将stack1中的字符逐个pop出来,insert到一个StringBuilder里组成结果(如果遇到'-',不是直接insert '-',而要insert从stack2中pop出来的decoded substring)。
递归
因为题目涉及很多重复子单元的重复嵌套,每个单元是一个encoded substring,递归方法能decode一个encoded substring,每当遇到数字,说明当前的encoded substring中还嵌套了encoded substring,调用自身来decode、append返回的字符串、遍历到嵌套的encoded substring的尾部,再继续向后遍历;
decode过程中,一旦遇到']'字符就停止,此时已经decode到了当前的encoded substring的结尾(结尾之前不会遇到']',即使中间有嵌套的encoded substring,它的']'会在遇到数字字符的那个判断中的while循环里被走完);如果arr走完,也停止decode,只有当s不是一个encoded substring的情况下(例如,s为"ab2[c]"或"2[abc]d"这种,而不是"2[ab2[c]]"这种)的第一次调用可能出现这个情况,其他时候的decode都应该是在']'处结束。
/**
* 自己的代码1,用stack来获取一对"[]"中间的字符串内容
* 感觉有点繁琐
* Runtime: 2 ms, faster than 43.37%
* Memory Usage: 39.1 MB, less than 12.56%
*/
class Solution {
public String decodeString(String s) {
LinkedList<Character> main = new LinkedList(); // store the sequence of traversed characters
LinkedList<String> subs = new LinkedList(); // store the decoded substrings
// decode "s"
for (char curr : s.toCharArray()) {
if (curr != ']') {
main.push(curr);
} else { // decode the nearest substring
StringBuilder sub = new StringBuilder();
int times = 0;
while (!main.isEmpty()) {
char c = main.pop();
if (c == '[') { // reach the start of the encoded substring
int digits = 0;
while (!main.isEmpty() && main.peek() >= '0' && main.peek() <= '9') { // get the times the substring should be repeated
times += (main.pop() - '0') * Math.pow(10, digits++);
}
break;
}
sub.insert(0, c == '-' ? subs.pop() : c);
}
String subString = sub.toString();
while (times-- > 1) {
sub.append(subString);
}
subs.push(sub.toString()); // store the decoded substring
main.push('-'); // mark that a decoded substring should be taken from Stack "subs"
}
}
// form "res" by combining all the characters and decoded substrings in sequence
StringBuilder res = new StringBuilder();
while (!main.isEmpty()) {
char c = main.pop();
res.insert(0, c == '-' ? subs.pop() : c);
}
return res.toString();
}
}
/**
* 自己的代码2,递归
* Runtime: 0 ms, faster than 100.00%
* Memory Usage: 39 MB, less than 12.45%
*/
class Solution {
public String decodeString(String s) {
char[] arr = s.toCharArray();
return decoder(arr, 0, 1); // consider "s" a encoded string that start at index 0 and should be repeated 1 time
}
private String decoder(char[] arr, int idx, int times) {
StringBuilder sb = new StringBuilder();
for (int i = idx; i < arr.length && arr[i] != ']'; i++) { // when i reach the end of arr(the encoded string is "s"), or arr[i] is ']'(the encoded string is "s"'s substring), current decoding is done
if (arr[i] <= '9' && arr[i] >= '0') { // encounter an encoded substring
// decode the substring and append it to sb
int subTimes = 0;
while (arr[i] <= '9' && arr[i] >= '0') {
subTimes = subTimes * 10 + arr[i++] - '0';
}
sb.append(decoder(arr, i + 1, subTimes));
// find the position after the encoded part
LinkedList<Character> stack = new LinkedList();
stack.push('[');
while (!stack.isEmpty()) {
i++;
if (arr[i] == '[') {
stack.push('[');
} else if (arr[i] == ']') {
stack.pop();
}
}
} else {
sb.append(arr[i]);
}
}
String sub = sb.toString();
while (times-- > 1) {
sb.append(sub);
}
return sb.toString();
}
}