leetcode394. Decode String

題目要求

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次。注意,源字符串中的括號是允許嵌套的,且展開的字符中不會包含任何數字。

思路一:遞歸

其實遞歸的思路是很明顯的,一旦我們遇到一個左括號,我們就可以找到其對應的右括號,然後對括號中的內容遞歸的展開,再將返回結果給上層,根據上次的次數再次展開。
3[a2[c]]=>3[acc]=>accaccacc

遞歸這裏需要注意的是如何找到當前括號對應的右括號。這裏可以採用一個小技巧,即從當前括號位置開始,每遇到一個左括號計數就+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的情形有如下幾種可能:

  1. rgt指向[
  2. rgt指向]
  3. rgt指向數字
  4. rgt指向字母

下面我們來逐個分析各種場景:

1. rgt指向[

此時左括號的左側只會有一種情形,它的左邊一定是數字。
因此當我們遇到左括號時,我們應當記錄左括號左邊的數字,並將lft指針移動到左括號下一個位置。這裏需要額外注意的是,如果當前該括號外圍存在父元素,則我們應當將父元素的計數和已有字符串壓入棧中。可以將這個操作理解爲上下文切換。

2. rgt指向]

右括號意味着當前的字符展開序列遍歷完畢,因此我們需要做以下幾件事情:

  1. 將lft和rgt之間的內容append到當前上下文的字符串中
  2. 根據展開次數展開當前上下文的字符串
  3. 如果存在父元素,則從棧中彈出父元素,恢復父級上下文
  4. 將當前上文得到的結果append到父元素的字符串中

3. rgt指向字母

我們需要將rgt指向的字母添加到當前的上下文字符串中去。不要忘記將左指針移動到當前位置後面

4. rgt指向數字

數字將會在遇見[時提取出來,因此我們無需進行任何處理

一個具體的例子

假如現在輸入的字符串爲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();
    }

clipboard.png
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注我的微信公衆號!將會不定期的發放福利哦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章