字符串编码

题目描述

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

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

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

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

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"
示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"
示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"
示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

解题思路

1. 利用栈

本题中可能出现括号嵌套的情况,比如 2[a2[bc]],这种情况下我们可以先转化成 2[abcbc],在转化成 abcbcabcbc。我们可以把字母、数字和括号看成是独立的 TOKEN,并用栈来维护这些 TOKEN。具体的做法是,遍历这个栈:

  • 如果当前的字符为数位,解析出一个数字(连续的多个数位)并进栈
  • 如果当前的字符为字母或者左括号,直接进栈
  • 如果当前的字符为右括号,开始出栈,一直到左括号出栈,出栈序列反转后拼接成一个字符串,此时取出栈顶的数字(此时栈顶一定是数字,想想为什么?),就是这个字符串应该出现的次数,我们根据这个次数和字符串构造出新的字符串并进栈

代码如下:

class Solution {
    private int index = 0;
    public String decodeString(String s) {
        LinkedList<String> stack = new LinkedList<String>();
        while(index < s.length()){
            char c = s.charAt(index);
            if(Character.isDigit(c)){
                int digit = getDigit(s);
                stack.addLast(String.valueOf(digit));
                continue;
            }
            if(c == ']'){
                StringBuilder tmpRes = new StringBuilder();
                //先弹出[xx]中的内容xx
                while(!stack.isEmpty() && !stack.peekLast().equals("[")){
                    
                    tmpRes.append(new StringBuilder(stack.removeLast()).reverse().toString());
                }
                tmpRes = tmpRes.reverse();
                //弹出左括号
                stack.removeLast();
                //再弹出数字
                int digit = Integer.parseInt(stack.removeLast());
                //重新拼装结果并重新入栈
               
               
                StringBuilder sb = new StringBuilder();
                for(int i = 0; i < digit; i ++){
                    sb.append(tmpRes);
                }
                stack.addLast(sb.toString());   
            }
            else{
                stack.addLast(c + "");
            }
            index ++;
        }

        //返回最终的结果,弹出栈中所有字符串
        StringBuilder decodeString = new StringBuilder();
        while(!stack.isEmpty()){
            decodeString.append(stack.removeFirst());
        }
        return decodeString.toString();
    }

    public int getDigit(String s){
        int digit = 0;
        char c = s.charAt(index);
        while(Character.isDigit(c)){
            digit = digit * 10 + (c - '0');
            index ++;
            c = s.charAt(index);
        }
        return digit;
    }
}

解题记录

  • 栈中: "kjkj" "e" "f"  目标:kjkjef
  • 弹栈然后拼接字符串再反转得到: f+e+kjkj (反转) jkjkef (不等于kjkjef)
  • 应在在弹栈的时候先一步reverse
  • f + e + jkjk(反转) kjkjef

复杂度分析

  • 时间复杂度:记解码后得出的字符串长度为 SS,除了遍历一次原字符串 ss,我们还需要将解码后的字符串中的每个字符都入栈,并最终拼接进答案中,故渐进时间复杂度为 O(S+|s|),即 O(S)。
  • 空间复杂度:记解码后得出的字符串长度为 SS,这里用栈维护 TOKEN,栈的总大小最终与 SS 相同,故渐进空间复杂度为 O(S)。

2. 递归解题

class Solution {
    private int index = 0;
    private String src;
    public String decodeString(String s) {
       src = s;
        return getString();
    }
    public String getString(){
        if(index == src.length() || src.charAt(index) == ']'){
            index ++;//过滤]
            return "";
        }
        char c = src.charAt(index);
        int digit = 1;
        StringBuilder sb = new StringBuilder();
        String ret = "";
        if(Character.isDigit(c)){
            digit = getDigit(src);
            index ++;//过滤[
            String str = getString();
            for(int i = 0; i < digit; i ++){
                sb.append(str);
            }
            ret = sb.toString();
        }
        else if(Character.isLetter(c)){
            ret = c + "";
            index ++;
        }
        return ret + getString();
    }
    public int getDigit(String s){
        int digit = 0;
        char c = s.charAt(index);
        while(Character.isDigit(c)){
            digit = digit * 10 + (c - '0');
            index ++;
            c = s.charAt(index);
        }
        return digit;
    }
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值