栈题目:字符串解码

题目

标题和出处

标题:字符串解码

出处:394. 字符串解码

难度

6 级

题目描述

要求

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

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

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

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

示例

示例 1:

输入: s   =   "3[a]2[bc]" \texttt{s = "3[a]2[bc]"} s = "3[a]2[bc]"
输出: "aaabcbc" \texttt{"aaabcbc"} "aaabcbc"

示例 2:

输入: s   =   "3[a2[c]]" \texttt{s = "3[a2[c]]"} s = "3[a2[c]]"
输出: "accaccacc" \texttt{"accaccacc"} "accaccacc"

示例 3:

输入: s   =   "2[abc]3[cd]ef" \texttt{s = "2[abc]3[cd]ef"} s = "2[abc]3[cd]ef"
输出: "abcabccdcdcdef" \texttt{"abcabccdcdcdef"} "abcabccdcdcdef"

示例 4:

输入: s   =   "abc3[cd]xyz" \texttt{s = "abc3[cd]xyz"} s = "abc3[cd]xyz"
输出: "abccdcdcdxyz" \texttt{"abccdcdcdxyz"} "abccdcdcdxyz"

数据范围

  • 1 ≤ s.length ≤ 30 \texttt{1} \le \texttt{s.length} \le \texttt{30} 1s.length30
  • s \texttt{s} s 只包含小写英语字母、数字和方括号 ‘[]’ \texttt{`[]'} ‘[]’
  • s \texttt{s} s 保证是有效的输入
  • s \texttt{s} s 中的所有整数的取值范围是 [1,   300] \texttt{[1, 300]} [1, 300]

解法一

思路和算法

编码字符串使用方括号表示需要重复的片段,方括号可能出现嵌套的情况,如果出现嵌套的情况,则按照从内层到外层的顺序解码。由于解码时需要根据每一对匹配的方括号进行解码,寻找匹配的方括号需要借助栈的数据结构,因此可以使用栈实现解码。

由于栈内存储的元素为字符,包括字母和方括号,因此可以使用 StringBuffer \texttt{StringBuffer} StringBuffer 类型的变量代替栈,以下称为「字符串栈」。除了字母和方括号以外,编码字符串中还包含数字,为了区分数字和非数字,因此另外使用一个栈存储数字,以下称为「数字栈」。从左到右遍历字符串 s s s,对于每种类型的字符,操作分别如下:

  • 当前字符是数字,则使用当前字符更新重复次数;

  • 当前字符是字母,则将当前字符入字符串栈;

  • 如果字符是左方括号,则一个数字遍历结束,将重复次数入数字栈,并将当前字符入字符串栈;

  • 如果字符是右方括号,则一对方括号遍历结束,根据当前的一对方括号和方括号前的数字解码,解码操作如下:

    1. 将字符串栈的字母依次出栈,直到遇到左方括号,用 temp \textit{temp} temp 存储出栈的字母,然后将左方括号从字符串栈出栈;

    2. temp \textit{temp} temp 反转,反转后的 temp \textit{temp} temp 中的字母顺序与进入字符串栈的顺序相同;

    3. 将数字栈的栈顶元素 k k k 出栈,则 temp \textit{temp} temp 需要在解码字符串中重复 k k k 次,因此执行 k k k 次将 temp \textit{temp} temp 入字符串栈的操作。

遍历结束之后,字符串栈按照从栈底到栈顶的顺序即为解码字符串。

考虑如下例子: s = “a3[b2[cd]]ef" s = \text{``a3[b2[cd]]ef"} s=“a3[b2[cd]]ef",编码字符串的长度为 12 12 12。解码操作如下。

下标 0 0 0 处的字符是字母,将字母入字符串栈。字符串栈为 “a" \text{``a"} “a",数字栈为 [ ] [] [],其中字符串栈用字符串代替栈,字符串栈和数字栈都是左边为栈底,右边为栈顶。

下标 1 1 1 处的字符是数字,重复次数更新为 3 3 3

下标 2 2 2 处的字符是左方括号,将重复次数入数字栈,并将左方括号入字符串栈。字符串栈为 “a[" \text{``a["} “a[",数字栈为 [ 3 ] [3] [3]

下标 3 3 3 处的字符是字母,将字母入字符串栈。字符串栈为 “a[b" \text{``a[b"} “a[b",数字栈为 [ 3 ] [3] [3]

下标 4 4 4 处的字符是数字,重复次数更新为 2 2 2

下标 5 5 5 处的字符是左方括号,将重复次数入数字栈,并将左方括号入字符串栈。字符串栈为 “a[b[" \text{``a[b["} “a[b[",数字栈为 [ 3 , 2 ] [3, 2] [3,2]

下标 6 6 6 7 7 7 处的字符都是字母,将字母入字符串栈。字符串栈为 “a[b[cd" \text{``a[b[cd"} “a[b[cd",数字栈为 [ 3 , 2 ] [3, 2] [3,2]

下标 8 8 8 处的字符是右方括号,执行如下操作。

  1. 将字符串栈的字母依次出栈直到遇到左方括号,然后将左方括号从字符串栈出栈, temp = “dc" \textit{temp} = \text{``dc"} temp=“dc"

  2. 将出栈的字母反转, temp = “cd" \textit{temp} = \text{``cd"} temp=“cd"

  3. 将数字栈的栈顶元素 k = 2 k = 2 k=2 出栈,执行 2 2 2 次将 “cd" \text{``cd"} “cd" 入字符串栈。字符串栈为 “a[bcdcd" \text{``a[bcdcd"} “a[bcdcd",数字栈为 [ 3 ] [3] [3]

下标 9 9 9 处的字符是右方括号,执行如下操作。

  1. 将字符串栈的字母依次出栈直到遇到左方括号,然后将左方括号从字符串栈出栈, temp = “dcdcb" \textit{temp} = \text{``dcdcb"} temp=“dcdcb"

  2. 将出栈的字母反转, temp = “bcdcd" \textit{temp} = \text{``bcdcd"} temp=“bcdcd"

  3. 将数字栈的栈顶元素 k = 3 k = 3 k=3 出栈,执行 3 3 3 次将 “bcdcd" \text{``bcdcd"} “bcdcd" 入字符串栈。字符串栈为 “abcdcdbcdcdbcdcd" \text{``abcdcdbcdcdbcdcd"} “abcdcdbcdcdbcdcd",数字栈为 [ ] [] []

下标 10 10 10 11 11 11 处的字符都是字母,将字母入字符串栈。字符串栈为 “abcdcdbcdcdbcdcdef" \text{``abcdcdbcdcdbcdcdef"} “abcdcdbcdcdbcdcdef",数字栈为 [ ] [] []

解码字符串为 “abcdcdbcdcdbcdcdef" \text{``abcdcdbcdcdbcdcdef"} “abcdcdbcdcdbcdcdef"

代码

class Solution {
    public String decodeString(String s) {
        StringBuffer sb = new StringBuffer();
        Deque<Integer> stack = new ArrayDeque<Integer>();
        int num = 0;
        int length = s.length();
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            if (Character.isDigit(c)) {
                num = num * 10 + c - '0';
            } else if (Character.isLetter(c)) {
                sb.append(c);
            } else if (c == '[') {
                stack.push(num);
                num = 0;
                sb.append(c);
            } else {
                int top = sb.length() - 1;
                StringBuffer temp = new StringBuffer();
                while (sb.charAt(top) != '[') {
                    temp.append(sb.charAt(top));
                    sb.deleteCharAt(top--);
                }
                sb.deleteCharAt(top--);
                temp.reverse();
                int k = stack.pop();
                for (int j = 0; j < k; j++) {
                    sb.append(temp);
                }
            }
        }
        return sb.toString();
    }
}

复杂度分析

  • 时间复杂度: O ( n e + n d ) O(n_e + n_d) O(ne+nd),其中 n e n_e ne 是编码字符串 s s s 的长度, n d n_d nd 是解码字符串的长度。需要遍历编码字符串 s s s 一次,解码过程中需要遍历解码字符串。

  • 空间复杂度: O ( n d ) O(n_d) O(nd),其中 n d n_d nd 是解码字符串的长度。空间复杂度主要取决于栈空间。

解法二

思路和算法

解法一使用栈实现解码,也可以用递归代替栈。

解法一的栈用于遇到数字和方括号的情况,递归处理的情况也是遇到数字和方括号的情况。从左到右遍历编码字符串 s s s,遇到数字时,得到重复次数,然后定位到左方括号的右边一个字符,调用递归。递归的终止条件是遇到右方括号,此时一对方括号遍历结束,根据方括号内的字母和重复次数解码。

代码

class Solution {
    int index = 0;

    public String decodeString(String s) {
        return recurse(s).toString();
    }

    public StringBuffer recurse(String s) {
        StringBuffer sb = new StringBuffer();
        int length = s.length();
        while (index < length) {
            char c = s.charAt(index);
            if (c == ']') {
                return sb;
            } else if (Character.isDigit(c)) {
                int num = 0;
                while (index < length && Character.isDigit(s.charAt(index))) {
                    num = num * 10 + s.charAt(index) - '0';
                    index++;
                }
                index++;
                StringBuffer temp = recurse(s);
                for (int i = 0; i < num; i++) {
                    sb.append(temp);
                }
            } else {
                sb.append(c);
            }
            index++;
        }
        return sb;
    }
}

复杂度分析

  • 时间复杂度: O ( n e + n d ) O(n_e + n_d) O(ne+nd),其中 n e n_e ne 是编码字符串 s s s 的长度, n d n_d nd 是解码字符串的长度。需要遍历编码字符串 s s s 一次,解码过程中需要遍历解码字符串。

  • 空间复杂度: O ( n d ) O(n_d) O(nd),其中 n d n_d nd 是解码字符串的长度。空间复杂度主要取决于递归调用的栈空间和存储解码字符串的 StringBuffer \texttt{StringBuffer} StringBuffer 类型的对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值