【算法挑战】字符串解码(含解析、源码)

394.字符串解码

https://leetcode-cn.com/problems/decode-string/

题目描述

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

编码规则为: 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".

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decode-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法 1: 递归

思路

在这里插入图片描述

n[string] 表示解析 [] 模板里面的内容,然后重复 n 次,即得到 n 个 string 拼接起来的字符串。

根据题意,[] 里面也是可以嵌套 [] 的,例如 n[m[string]]。这种情况下,我们得先解析最内层的模板,重复 m 次,然后将 m * string 的结果作为外层模板的解析内容,再重复 n 次。

如果嵌套的层数更多,我们也是得先找到最内层的 [],就像洋葱一样,一层一层地剥开,然后再从内到外一层一层地解析和拼接。这种描述很容易就让人想到了递归。

看代码注释吧。

复杂度分析

  • 时间复杂度: O ( S ) O(S) O(S),S 是解析后字符串的长度。
  • 空间复杂度: O ( S ) O(S) O(S),S 是解析后字符串的长度,递归栈空间。

代码

const type = {
    isAlpha: s => /[a-z]/i.test(s),
    isDigit: s => /[0-9]/.test(s),
    isOpenParen: s => s === '[',
    isCloseParen: s => s === ']',
};

/**
 * @param {string} s
 * @return {string}
 */
var decodeString = function (s, i = 0) {
    // 从 i 开始遍历字符串

    let decoded = ''; // 解密字符串
    let cnt = ''; // 累计次数

    while (i < s.length) {
        if (type.isAlpha(s[i])) {
            // 普通字符,直接拼接到 decoded
            decoded += s[i];
            i++;
        } else if (type.isDigit(s[i])) {
            // 数字,拼接到 cnt
            cnt += s[i];
            i++;
        } else if (type.isOpenParen(s[i])) {
            // 遇到开括号,就把括号内的字符串重复 cnt 次,再拼接到 decoded
            // 但括号内可能存在嵌套括号,所以需要递归处理
            // 我们需要从递归中取两个东西,1.括号内解析后的模式,2.这个开括号对应的右括号的下标,下次遍历字符串就从这个下标+1开始
            const [pattern, nextIndex] = decodeString(s, i + 1);
            // 重复 cnt 次拼接到 decoded
            decoded += pattern.repeat(Number(cnt));

            cnt = '';
            i = nextIndex;
            continue;
        } else if (type.isCloseParen(s[i])) {
            // 遇到闭括号,说明括号内的模式解析完毕
            // 递归结束,返回我们需要的东西:1.解析后的字符串,2.解析到的字符下标
            return [decoded, i + 1];
        }
    }
    return decoded;
};

C++ Code

class Solution {
private:
    int ptr_ = 0;
public:
    string decodeString(string s) {
      string decoded_str = "";
      string repeat_times = "";

      int i = ptr_;
      while (i < s.length()) {
          if (isalpha(s[i])) {
              decoded_str += s[i];
              i++;
          } else if (isdigit(s[i])) {
              repeat_times += s[i];
              i++;
          } else if (s.compare(i, 1, "[") == 0) {
              ptr_ = i + 1;
              string pattern = decodeString(s);
              i = ptr_;
  
              int times = stoi(repeat_times);
              for (int t = 0; t < times; t++) {
                  decoded_str += pattern;
              }
              repeat_times = "";
          } else if (s.compare(i, 1, "]") == 0) {
              ptr_ = i + 1;
              return decoded_str;
          }
      }
      return decoded_str;
    }
};

方法 2: 循环 + 栈

可以用递归解决的问题,也可以用循环来解决。

这里我用了正则 /[a-zA-Z]+|[0-9]+|\[|\]/exec() 方法来遍历字符串并把它们拆分成 token,比如,lz3[ab2[c]] 会被拆分成 lz, 3, [, ab, 2, [, c, ], ]

  1. 遇到字母块 (lz)、数字时,入栈;
  2. 遇到 [ 时,入栈,用来标识当前进入一个模板解析了;
  3. 遇到 ] 时,说明当前模板遍历完了,我们可以开始解析了。开始出栈,把出栈的字母块都拼接起来,等出栈到 [ 时,说明当前模板解析完成了。继续出栈一个元素,这个元素就是当前模板要重复的次数,把"字母块 * 次数"后推入栈中。之所以要推入栈中是因为模板是可以嵌套的,当前模板的外层可以还是一个模板,所以我们要把结果放回去,继续解析外层的模板。

图解

复杂度分析

  • 时间复杂度: O ( S ) O(S) O(S),S 是解析后字符串的长度。
  • 空间复杂度: O ( S ) O(S) O(S),S 是解析后字符串的长度。

代码

/**
 * @param {string} s
 * @return {string}
 */
var decodeString = function (s) {
    const reg = /[a-zA-Z]+|[0-9]+|\[|\]/g;
    const stack = [];
    const peek = () => stack[stack.length - 1]; // p.s. 不正经栈

    while (reg.lastIndex < s.length) {
        let token = reg.exec(s)[0];
        if (token !== ']') {
            // 数字,字母,左括号通通入栈
            stack.push(token);
        } else {
            // 遇到右括号就开始出栈
            let str = '';
            // [] 中间的就是要重复的模式,把它们全部出栈,拼接起来
            while (peek() !== '[') {
                str = stack.pop() + str;
            }
            // 丢掉左括号
            stack.pop();
            // 左括号前面的一定是模式重复的次数
            const num = +stack.pop();
            // 把复制操作后的字符串放回栈中,作为外层 [] 模式的一部分
            stack.push(str.repeat(num));
        }
    }
    return stack.join('');
};

总结

以上就是本文所有内容了,希望能对你有所帮助,能够解决字符串解码问题。

如果你喜欢本文,也请务必点赞、收藏、评论、转发,这会对我有非常大的帮助。请我喝杯冰可乐也是极好的!

已完结,欢迎持续关注。下次见~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
fastjson 是一个 Java 库,用于将 JSON 字符串解析为 Java 对象或将 Java 对象序列化为 JSON 字符串。其解析 JSON 字符串算法原理如下: 1. 首先,fastjson 将 JSON 字符串转换为一个字符数组。 2. fastjson 会遍历字符数组中的每个字符,直到解析完整个 JSON 字符串。 3. 如果遇到左花括号 `{`,则表示开始一个新的 JSON 对象,fastjson 会创建一个新的 Java 对象来保存该 JSON 对象的属性。 4. 如果遇到左方括号 `[`,则表示开始一个新的 JSON 数组,fastjson 会创建一个新的 Java 数组来保存该 JSON 数组的元素。 5. 如果遇到双引号 `"`,则表示开始一个 JSON 字符串,fastjson 会读取该字符串的内容并将其存储在 Java 对象的属性中。 6. 如果遇到数字或布尔值,则表示一个 JSON 数字或布尔值,fastjson 会将其转换为对应的 Java 类型并存储在 Java 对象的属性中。 7. 如果遇到 null,则表示一个空值,fastjson 会将其转换为 Java 中的 null 值并存储在 Java 对象的属性中。 8. 如果遇到逗号 `,`,则表示一个属性或元素的结束,fastjson 会继续解析下一个属性或元素。 9. 如果遇到右花括号 `}` 或右方括号 `]`,则表示当前 JSON 对象或数组的结束,fastjson 会返回解析后的 Java 对象或数组。 通过以上的步骤,fastjson 就能够将 JSON 字符串解析为对应的 Java 对象或数组。这个算法原理是比较常见的 JSON 解析方法,类似的原理也被用于其他的 JSON 解析库中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sanbaofengs

请我喝一杯冰可乐吧~

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

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

打赏作者

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

抵扣说明:

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

余额充值