【LeetCode】394. 字符串解码:给定一个经过编码的字符串,返回它解码后的字符串

题目描述:给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数(多位数)。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例:

s = “3[a]2[bc]”, 返回 “aaabcbc”.
s = “3[a2[c]]”, 返回 “accaccacc”.
s = “2[abc]3[cd]ef”, 返回 “abcabccdcdcdef”.

方法一:括号匹配问题用栈,这里用了两个栈

/**
 * 解法一
 *
 * 利用2个栈,一个数字栈numStack,一个字母栈strStack
 * 遍历字符串
 * 1、字符为数字,解析数字(注意连续数字的情况)存入 num
 * 2、字符为字母,拼接字母 存入 str
 * 3、字符为左括号,把之前得到的数字 num 和 字母 str 分别压栈,然后把数字重置为0,字母字符串重置为空串
 * 4、字符为右括号,数字栈栈顶数字出栈,作为重复次数 n,字母栈栈顶字母出栈,作为前缀字母字符串去拼接 str 字母变量,总共拼接 n 次,拼接后的新字母串给 str
 *
 * 例如:
 * 2[abc]3[cd]ef
 *  ↑
 * 遇到左括号,把数字 num=2 和 字母 str="" 入栈,并且 num 和 str 重置
 *    |   |      |    |
 *    |   |      |    |
 *    |_2_|      |_""_|
 *   numStack    strStack
 *
 * 2[abc]3[cd]ef
 *      ↑
 * 遇到左括号,num=0 str="abc",numStack 和 strStack 栈顶元素出栈 str = strStack.pop() + str \* numStack.pop() = "" + "abc" * 2 =  "abcabc"
 *    |   |      |   |
 *    |   |      |   |
 *    |___|      |___|
 *   numStack    strStack
 *
 * 2[abc]3[cd]ef
 *        ↑
 * 遇到右括号,数字 num=3 和 字母 str="abcabc" 入栈,并且 num 和 str 重置
 *    |   |      |        |
 *    |   |      |        |
 *    |_3_|      |_abcabc_|
 *   numStack    strStack
 *
 * 2[abc]3[cd]ef
 *           ↑
 * 遇到左括号,num=0 str=cd,numStack 和 strStack 栈顶元素出栈 str = "abcabc" + "cd" * 3 = "abcabccdcdcd"
 *    |   |      |        |
 *    |   |      |        |
 *    |_3_|      |_abcabc_|
 *   numStack    strStack
 *
 * 遍历结束,最终结果 str="abcabccdcdcdef"
 */
package leetcode;

import java.util.Stack;

public class leetcode_394 {

        public static String decodeString(String s) {
            Stack<String> stack = new Stack<>();
            Stack<Integer> nums = new Stack<>();
            StringBuilder res = new StringBuilder();
            // res = "";
            StringBuilder t = new StringBuilder();
            int num = 0;
            for(int i = 0; i < s.length(); i++) {

                char ch = s.charAt(i);
                // 如果遇到数字(数字范围可能是比较大),入栈
                if(ch >= '0' && ch <= '9') {
                    num = num * 10 + ch - '0';
                }else if(ch == '['){
                    //遇到"[",将倍数入栈,就把之前的字符串t入栈,并将num清零,重新创建新的字符串t用于存放下一个需要加倍的新串
                    if(num > 0) {
                        nums.push(num);
                    }
                    stack.push(t.toString());
                    //清零+重新创建新的待加倍的字符串
                    t = new StringBuilder();
                    num = 0;
                } else if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z' )) {
                    // 如果遇到字母,拼接到距离"["最近的当前待加倍的字符串t中
                    t.append(ch);
                } else if(ch == ']') {
                    // 遍历如果遇到"]",从字符串栈中取出一个字符串,从数字栈取出一个数字,将字符串进行加倍
                    //sb 存放去掉这层k[]后,再外面一层[]中的结果
                    StringBuilder sb = new StringBuilder();
                    String str = stack.pop();
                    sb.append(str);
                    int number = nums.pop();
                    for(int j = 0; j < number; j++) {
                        sb.append(t);
                    }
                    // 加倍后的字符串存放到[]外层的待加倍字符串,继续扫描,看当前]后面是什么字符
                    t = sb;
                }

            }
            return t.toString();
        }

    public static void main(String[] args) {
        System.out.println(decodeString("3[a]2[bc]"));
    }
}

方法二:递归法

总体思路与辅助栈法一致,不同点在于将 [ 和 ] 分别作为递归的开启与终止条件:

  • 当 s[i] == ‘]’ 时,返回当前括号内记录的 res 字符串与 ] 的索引 i (更新上层递归指针位置);
  • 当 s[i] == ‘[’ 时,开启新一层递归,记录此 […] 内字符串 tmp 和递归后的最新索引 i,并执行 res + multi * tmp 拼接字符串。
    遍历完毕后返回 res。

复杂度分析:

  • 时间复杂度 O(N),递归会更新索引,因此实际上还是一次遍历 s;
  • 空间复杂度 O(N),极端情况下递归深度将会达到线性级别。
class Solution {
    public String decodeString(String s) {
        return dfs(s, 0)[0];
    }
    private String[] dfs(String s, int i) {
        StringBuilder res = new StringBuilder();
        int multi = 0;
        while(i < s.length()) {
            if(s.charAt(i) >= '0' && s.charAt(i) <= '9') 
                multi = multi * 10 + Integer.parseInt(String.valueOf(s.charAt(i))); 
            else if(s.charAt(i) == '[') {
                String[] tmp = dfs(s, i + 1);
                i = Integer.parseInt(tmp[0]);
                while(multi > 0) {
                    res.append(tmp[1]);
                    multi--;
                }
            }
            else if(s.charAt(i) == ']') 
                return new String[] { String.valueOf(i), res.toString() };
            else 
                res.append(String.valueOf(s.charAt(i)));
            i++;
        }
        return new String[] { res.toString() };
    } 
}

其他好理解的思路:
https://leetcode-cn.com/problems/decode-string/solution/java-gua-hao-pi-pei-wen-ti-yong-zhan-by-joseph-4/

你可能感兴趣的:(LeetCode)