LeetCode 394. 字符串解码(Java代码)

前往LeetCode做题

题目描述

给定一个经过编码的字符串,返回它解码后的字符串。

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

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

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

输入输出样例

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

辅助栈

需要以下几个变量:

  • 乘数栈:存储3、11、2这样的乘数
  • 被乘字符串栈:存储每一阶段需要被乘的字符串
  • 乘数:暂存乘数
  • 当前字串:

循环遍历经过编码的字符串

  • 如果碰到 [ ,就将乘数入栈,同时将当前字串如栈
    入栈以后,将原来的变量或置0,或清空(重赋值)
  • 如果碰到 ],将栈中乘数弹出,用新变量存储
    将当前字串乘以操作数,再将栈顶弹出,和它连接
  • 如果碰到数字字符,可能会是连续的,如12,所以。。。
  • 如果碰到的是其它字符,一律让此字符与 当前字串拼接

代码:

class Solution {
     
    public String decodeString(String s) {
     
        // 存储乘数的栈
        Stack<Integer> stack_multi = new Stack<Integer>();
        // 存储被乘字符串的字符串
        Stack<String> stack_str = new Stack<String>();
        // 乘数
        int multi = 0;
        // 当前子串
        StringBuilder ret = new StringBuilder();

        // 开始遍历字符串
        for(Character c : s.toCharArray()){
     
            if(c == '['){
     
                // 将乘数入栈,并置0
                stack_multi.push(multi);
                multi = 0;
                // 将当前字串入栈,并重赋值
                stack_str.push(ret.toString());
                ret = new StringBuilder();
            }
            else if(c == ']'){
     
                // 乘数弹出,用新变量存储
                int cur_multi = stack_multi.pop();
                // 当前字串乘以乘数
                StringBuilder temp = new StringBuilder();
                for(int i=0; i<cur_multi; i++) temp.append(ret);
                // 将栈顶弹出,并和拼接好的字串连接
                ret = new StringBuilder(stack_str.pop()+temp);
            }
            else if(c >= '0' && c <= '9') multi = multi*10 + Integer.parseInt(c+"");
            else
                ret.append(c);
        }
        return ret.toString();
    }
}

递归法——使用全局变量

思路
[ 是开启递归的条件,此时需要存储 此刻的multi和ret
] 是结束递归的条件,此时直接返回即可。

以2[a3[bc]] 为例:
---------------------------
当前遍历		multi(乘数)		ret(当前字串)
   '2'		   0			    ""
   '[' 		----------开启递归----------		存储本地值:cur_multi(2)、cur_str("")
   置空		   0				""
   'a'		   0				"a"
   '3'		   3				"a"
   '[' 		----------开启递归----------		存储本地值:cur_multi(3)、cur_str("a")
    'b'		   0				"b"
    'c'		   0				"bc"
   ']' 		----------结束递归----------		使用本地值:cur_multi(3)、cur_str("a")
   使用本地值   0				"abcbcbc"
   ']' 		----------结束递归----------		使用本地值:cur_multi(2)、cur_str("")
   使用本地值   0				"abcbcbcabcbcbc"

递归的时候,我使用了全局变量,这个决定让我的编码变得简单了,可以直接将上面辅助栈的代码拿来用。

但是运行速度上还是有点不足的

代码:

class Solution {
     
    // 乘数
    int multi = 0;
    // 当前子串
    StringBuilder ret = new StringBuilder();
    // 索引
    int index = 0;
    public String decodeString(String s) {
     
        while(index < s.length()){
     
            char c = s.charAt(index++);
            if(c == '['){
     
                // 将乘数入栈,并置0
                int cur_multi = multi;
                multi = 0;
                // 将当前字串入栈,并重赋值
                String cur_str = ret.toString();
                ret = new StringBuilder();
                // 开启递归
                decodeString(s);
                // 递归返回之后
                
                // 乘数弹出,用新变量存储
                // ...
                // 当前字串乘以乘数
                StringBuilder temp = new StringBuilder();
                for(int i=0; i<cur_multi; i++) temp.append(ret);
                // 将栈顶弹出,并和拼接好的字串连接
                ret = new StringBuilder(cur_str+temp);
            }
            else if(c == ']'){
     
                return null;
            }
            else if(c >= '0' && c <= '9') multi = multi*10 + Integer.parseInt(c+"");
            else
                ret.append(c);
        }

        return ret.toString();
    }
}

代码基本完全就是复制辅助栈法的

递归法——使用局部变量

思路基本一致,只是使用局部变量的时候,需要往回传参,但是不再需要保存递归前的全局变量了。

class Solution {
     /(O(n),O(n))
//当 s[i] == ']' 时,返回当前括号内记录的 res 字符串与 ] 的索引 i (更新上层递归指针位置);
//当 s[i] == '[' 时,开启新一层递归,记录此 [...] 内字符串 tmp 和递归后的最新索引 i,并执行 res + multi * tmp 拼接字符串。
//遍历完毕后返回 res。
    public String decodeString(String s) {
     
        return dfs(s, 0)[1];
    }
    private String[] dfs(String s, int i) {
     //字符串s和索引i,返回字符数组,索引i和结果string
        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]);//遇到']',i=结尾索引,即右括号位置
                while(multi>0){
     
                    res.append(tmp[1]);
                    multi--;
                }
            }else if(s.charAt(i)==']'){
     
                return new String[] {
     String.valueOf(i),res.toString()};
            }else{
     
                res.append(s.charAt(i));
            }
            i++;
        }
        return new String[] {
     String.valueOf(i),res.toString()};
    } 
}

总结

  • 能用迭代解决的问题,大多数时候递归都能够搞定。反之,也是一样的。
  • 使用递归的时候,应该避免使用全局变量。全局变量容易让程序变得松散。

你可能感兴趣的:(LeetCode,字符串,栈,dfs,leetcode,数据结构)