Given an encoded string, return it's decoded string. The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer. You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 2[4]. Examples: s = "3[a]2[bc]", return "aaabcbc". s = "3[a2[c]]", return "accaccacc". s = "2[abc]3[cd]ef", return "abcabccdcdcdef".
将一个字符串解码,要求按照次数展开原字符串中的中括号。如3[a]2[bc]
对应的字符串就是aaabcbc
,即a展开3次,bc展开2次。注意,源字符串中的括号是容许嵌套的,且展开的字符中不会包含任何数字。java
其实递归的思路是很明显的,一旦咱们遇到一个左括号,咱们就能够找到其对应的右括号,而后对括号中的内容递归的展开,再将返回结果给上层,根据上次的次数再次展开。
如3[a2[c]]
=>3[acc]
=>accaccacc
。git
递归这里须要注意的是如何找到当前括号对应的右括号。这里能够采用一个小技巧,即从当前括号位置开始,每遇到一个左括号计数就+1,遇到一个右括号计数就-1,当计数器第一次被减为0时,则该位置上的右括号就是咱们所要找的对应的右括号。面试
代码以下:微信
public String decodeString2(String s) { if (s.length() == 0) return ""; StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c >= '0' && c <= '9') { // 解析次数 int digitStart = i++; while (s.charAt(i) >= '0' && s.charAt(i) <= '9') i++; int num = Integer.parseInt(s.substring(digitStart, i)); // 找到对应的右括号 int strStart = i+1; // number must be followed by '[' int count = 1; i++; while (count != 0) { if (s.charAt(i) == '[') count++; else if (s.charAt(i) == ']') count--; i++; } i--; // 取子字符串 String subStr = s.substring(strStart, i); // 将子字符串解码 String decodeStr = decodeString(subStr); // 将解码的结果拼接到当前的字符串后面 for (int j = 0; j < num; j++) { sb.append(decodeStr); } } else { // 添加首元素 sb.append(c); } } return sb.toString(); }
咱们知道,有一些递归的思路是能够转化为栈的,这里一样如此。利用栈咱们能够存储外围层已持有的字符串以及应当展开的次数。用栈的思路来写要求思路更加严谨,以避免出现逻辑错误。首先,咱们会用两个指针lft,rgt分别来记录数字的起始位置和结束位置。同时,rgt还做为咱们遍历整个字符串的指针。rgt的情形有以下几种可能:app
下面咱们来逐个分析各类场景:ide
此时左括号的左侧只会有一种情形,它的左边必定是数字。
所以当咱们遇到左括号时,咱们应当记录左括号左边的数字,并将lft指针移动到左括号下一个位置。这里须要额外注意的是,若是当前该括号外围存在父元素,则咱们应当将父元素的计数和已有字符串压入栈中。能够将这个操做理解为上下文切换。ui
右括号意味着当前的字符展开序列遍历完毕,所以咱们须要作如下几件事情:spa
咱们须要将rgt指向的字母添加到当前的上下文字符串中去。不要忘记将左指针移动到当前位置后面指针
数字将会在碰见[
时提取出来,所以咱们无需进行任何处理code
假如如今输入的字符串为3[a2[c]]
,咱们有字符串栈s,计数栈c,指针lft,rgt,并用临时变量tmp,number分别记录当前上下文中计数和字符串。运行状况以下:
lft=0 rgt=0 : 不执行任何操做 lft=0 rgt=1 : 解析当前上下文应当展开的次数 number=3, lft=2 lft=2 rgt=2 : 将当前的字符添加到当前的上下文中去,tmp="a" lft=3 lft=3 rgt=3 : 不作任何处理 lft=3 rgt=4 : 将父级上下文压入栈中,并解析当前上下文的展开次数 s:["a"] c:[3] lft=5 tmp="" number=2 lft=5 rgt=5 : 将当前的字符添加到当前的上下文中去,tmp="c" lft=6 lft=6 rgt=6 : 展开当前字符串,并恢复父级上下文, tmp="a"+"cc", number=3 s:[] c:[] lft=7 lft=7 rgt=7 : 展开当前字符串,= 此时没有父级上下文,所以无需恢复。tmp="accaccacc" number=0
代码以下:
public String decodeString(String s) { Stack<Integer> count = new Stack<>(); Stack<StringBuilder> letters = new Stack<>(); int lft = 0, rgt = -1; int number = 0; StringBuilder result = null; while(++rgt < s.length()) { char c = s.charAt(rgt); if(c == '[') { if(result != null) { count.push(number); letters.push(result); } result = new StringBuilder(); number = Integer.valueOf(s.substring(lft, rgt)); lft = rgt+1; }else if(c == ']') { result.append(s.substring(lft, rgt)); StringBuilder tmp = new StringBuilder(result); for(int i = 0 ; i<number-1 ; i++) { result.append(tmp); } if(!letters.isEmpty()) { StringBuilder pop = letters.pop(); pop.append(result); result = pop; number = count.pop(); } lft = rgt+1; }else if(Character.isAlphabetic(c)) { if(result==null) { result = new StringBuilder(); } result.append(c); lft = rgt+1; } } if(result == null) { result = new StringBuilder(); } result.append(s.substring(lft, rgt)); return result.toString(); }
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注个人微信公众号!将会不按期的发放福利哦~