算法:字符串解码


题目:字符串解码

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

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

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

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

1
2
3
4
5
示例:

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

分析

方法一

首先讲一下我拿到这道题的思路:递归。每一歌中括号可以是一层,然后递归返回中括号中的字符串,和外层的数字进行循环重复后继续下一个中括号。例如:2[4[ab]]3[b]

  1. 先得到2,然后递归进入第一个中括号
  2. 得到4,递归进入
  3. 得到ab,遇到]返回上一层
  4. ab进行4次重复,遇到]返回上一层
  5. 把得到的字符串进行2次重复。然后继续下一组。

思路应该是挺清晰的。但是这里有几个问题:在第五步的时候怎么确定下一组开始的位置?每一步递归,怎么确认开始的位置?我当时就卡在第一个问题,不知道如何解决。

对于第二个问题,很简单,重新写一个方法来递归,然后加入position参数即可。第一个问题有两个思路:

  1. 全局变量
  2. 在返回参数中加入

我比较喜欢第二种。因为全局变量就破坏了封装,我觉得不可取。但是返回参数,不是字符串吗,怎么附带位置信息?这里有个很好的解决方案:字符串数组。返回的字符串数组中一个保存得到的字符串,一个是数字字符串,再进行转换就可以得到位置信息了。思路到此应该就差不多了。另外的实现细节可以自己补充。

方法二

有递归,那么和递归一样思路的是什么?没错就是栈,那么我们可以使用一个辅助栈解决这个问题。思路和递归是差不多的:

  1. 遍历字符串数组并把字符放进栈。
  2. 当遍历到]时进行回溯,把中距离栈顶第一个[前的字符串拿出来
  3. 然后再遍历数字,遇到栈底或者]停止
  4. 然后进行循环运算后把字符串放进栈中
  5. 最后返回栈中的字符串

解答

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fun decodeString(s: String): String{
return getStrings(s,0)[0]
}
fun getStrings(s:String,i:Int):StringArray{
val stringBuilder = StringBuilder()
var num = 0
var position = i
while (position<s.length){
if (s[position] in 'a'..'z' || s[position] in 'A'..'Z'){
stringBuilder.append(s[position])
}else if (s[position] in '0'..'9'){
num = num*10 + s[position].toInt()-48
}else if (s[position]=='['){
val stringArray = getStrings(s,position+1)
position = stringArray[1].toInt()
while (num!=0){
stringBuilder.append(stringArray[0])
num--
}
}else if (s[position]==']'){
val stringArray = StringArray()
stringArray.add(stringBuilder.toString())
stringArray.add(position.toString())
return stringArray
}
position++
}
val stringArray = StringArray()
stringArray.add(stringBuilder.toString())
stringArray.add(position.toString())
return stringArray
}

方法二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
fun decodeString(s: String): String{
if (s.isEmpty()) return ""
val stack = Stack<Char>()
for (c in s){
if (c == ']'){
val stringBuilder = StringBuilder()
while (stack.peek()!='['){
stringBuilder.append(stack.pop())
}
stack.pop()
val string = stringBuilder.reverse().toString()

var num = 0
var i = 0
while (stack.isNotEmpty() && stack.peek()>='0' && stack.peek()<='9'){
val topNum = stack.pop().toInt()-48
val p = 10.toDouble().pow(i)
num += topNum*p.toInt()
i++
}

for (j in 1..num){
stack.addAll(string.toList())
}
}else stack.add(c)
}
val sb = StringBuilder()
while (stack.isNotEmpty()){
sb.append(stack.pop())
}
sb.reverse()
return sb.toString()
}

复杂度分析

假如最终的字符串长度为n,原字符串的长度为k

方法一

  1. 时间复杂度:遍历原字符串,还需要把新字符一个个拼进来。

时间复杂度:O(n+k)

  1. 空间复杂度:这里所需要的空间是递归栈的深度,最坏情况下为k

空间复杂度:O(k)

方法二

  1. 时间复杂度:除了遍历一次原字符串,还要把每个字符都放进栈中所以是

时间复杂度:O(n+k)

  1. 空间复杂度:需要一个栈来保存数据,栈的长度为字符串的长度

空间复杂度:O(n)


文章作者: zhegnhuan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zhegnhuan !
 上一篇
算法:柱状图中最大的矩形 算法:柱状图中最大的矩形
题目:柱状图中最大的矩形给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。 以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,
2020-05-29
下一篇 
算法:和可被k整除的子数组数目 算法:和可被k整除的子数组数目
题目:和可被k整除的子数组数目给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。 1234567示例:输入:A = [4,5,0,-2,-3,1], K = 5输出:7解释:有 7 个子数
2020-05-27
  目录