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(); }
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注個人微信公衆號!將會不按期的發放福利哦~