575. 表达式展开

描述

给出一个表达式s,此表达式包括数字,字母以及方括号。在方括号前的数字表示方括号内容的重复次数(括号内的内容可以是字符串或另一个表达式),请将这个表达式展开成一个字符串。

样例

S =abc3[a]返回abcaaa
S =3[abc]返回abcabcabc
S =4[ac]dy返回acacacacdy
S =3[2[ad]3[pf]]xyz
返回adadpfpfpfadadpfpfpfadadpfpfpfxyz

挑战

你可以不通过递归的方式完成展开吗?

代码

方法1. 非递归 + Stack 从最右边的一组括号开始展开
思路
在从左到右扫描字符串时,当前字符要分4种情况:

  1. 数字,可以不止一位,计算出完整数字
  2. 左括号,碰到左括号将当前 number 压入栈存起来,因为括号内也可能有数字,所以要将 number 先清零,然后用来存括号内的数字
  3. 右括号,碰到右括号表示某一完整括号的结束,要将其中内容展开,然后重新压入栈,所以非递归解法是从最右边的一组括号开始展开的
  4. 普通字符,直接压入栈中
// 栈中只压人字符和数字
public class Solution {
    /**
     * @param s  an expression includes numbers, letters and brackets
     * @return a string
     */
    public String expressionExpand(String s) {
        Stack stack = new Stack<>();
        int number = 0;

        for (char c : s.toCharArray()) {
            // 判断字符是否是数字
            if (Character.isDigit(c)) {
                // 注意 c 不能加''
                number = number * 10 + c - '0';
            } else if (c == '[') {
                // 将 number 强制转换成 Integer 类型压入栈,同时 number 归 0
                stack.push(Integer.valueOf(number));
                number = 0;
            // 每遇到一个右括号就把当前需要扩展的字符串扩展完压入栈
            } else if (c == ']') {
                String newStr = popStack(stack);
                Integer count = (Integer) stack.pop();
                // java 自动拆箱将 count 由 Integer 转为int
                for (int i = 0; i < count; i++) {
                    stack.push(newStr);
                }
            // 字符情况
            } else {
                // String.valueOf(c) 将 c 强制转换成字符串类型
                stack.push(String.valueOf(c));
            }
        }
        return popStack(stack);
    }

    // popstack 将压入 stack 栈中的字符串以原来顺序输出,注意不包括数字
    // 之前压入到栈中的顺序是反的,用 buffer 栈把顺序还原
    private String popStack(Stack stack) {
        Stack buffer = new Stack<>();
        // stack.peek() 查看栈顶元素而不移除它
        // stack 非空且栈顶元素是 String 类型
        // 把遇到 number 之前的栈顶所有字符全部压入 buffer 栈
        while (!stack.isEmpty() && (stack.peek() instanceof String)) {
            buffer.push((String) stack.pop());
        }
        // StringBuilder动态字符串
        StringBuilder sb = new StringBuilder();
        // 将压入 buffer 中的字符全部添加到动态字符串 sb 中
        while (!buffer.isEmpty()) {
            sb.append(buffer.pop());
        }
        // String 是不可变对象,而 StringBuilder 可变
        return sb.toString();
    }
}
 
 

关于String和StringBuilder区别:
http://www.cnblogs.com/dolphin0520/p/3778589.html

方法2. 递归
思路
递归和非递归一样,需要对当前字符做4种情况的分析,但在4种情况的基础上要额外判断当前字符是否属于当前递归最外围,是否应该参与展开运算;还是处于当前递归最内层,只是单纯把它记录下来,用于下一轮递归

/* 思路:
 * 1.split成几个部分
 * 2.每一部分分别recursion
 * 3.concat

 * 递归嵌套太深可能会出现系统栈溢出
 * 自己定义栈就不会出现这种问题
 * 内存Mermory :
 *             1.Heap Mermory:new int[100], new TreeNode()
 *             2.Stack:
 * 样例:在一个函数中新建一个结点
 * ListNode node = new ListNode(0);
 * ListNode(0)存在heap mermory中
 * reference存在stack中
 */

public class Solution {
    /**
     * @param s  an expression includes numbers, letters and brackets
     * @return a string
     */
    public String expressionExpand(String s) {
        int number = 0;
        // paren 是用来记录读到当前的 c 时,有多少还未配对的左括号
        int paren = 0;
        String subString = "";
        StringBuilder sb = new StringBuilder(); 

        for (char c : s.toCharArray()) {
            if (c == '[') {
                /* paren > 0证明当前字符 c 已经在一个括号里了,
                 * 需要把 c 用 subString 记录下来,方便递归调用
                 */
                if (paren > 0) {
                    /* 在原有的 subString 基础上添加新的字符,生成新的 subString
                     * 字符串
                     */
                    subString = subString + c;
                }
                paren++;
            } else if (c == ']') {
                paren--;
                // paren == 0说明到了当前递归层最外围的括号,括号内部要递归
                // 同一层内部按照从左往右顺序展开
                if (paren == 0) {
                    // push number * substring to sb
                    // 调用递归
                    String expandedString = expressionExpand(subString);
                    for (int i = 0; i < number; i++) {
                        sb.append(expandedString);
                    }
                    number = 0;
                    // 同一层递归可能有多组括号,所以subString要清0
                    subString = "";
                // 未到最外围括号,单纯记录右括号
                } else {
                    subString = subString + c;
                }
            } else if (c >= '0' && c <= '9') {
                /* 在本轮递归中数字一定不能在括号中,括号中的数字只是单纯
                 * 记录下来,括号外的才可以进行字符串多倍扩展
                 */
                if (paren == 0) {
                    number = number * 10 + c - '0';
                } else {
                    subString = subString + c;
                }
            // c 既不是数字也不是括号,只是字符
            } else {
                // 所有括号之后最结尾的字符
                if (paren == 0) {
                    /* valueOf() 方法用于返回给定参数的原生 Number 
                     * 对象值,参数可以是原生数据类型, String等
                     */
                    sb.append(String.valueOf(c));
                // 括号中的字符
                } else {
                    subString = subString + c;
                }
            }
        }

        return sb.toString();
    }
}

你可能感兴趣的:(575. 表达式展开)