LeedCode 1209: 删除字符串中的所有相邻重复项 II

删除字符串中的所有相邻重复项 II

题目描述:

给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。

你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。

在执行完所有删除操作后,返回最终得到的字符串。

本题答案保证唯一。

示例1

输入:s = "abcd", k = 2
输出:"abcd"
解释:没有要删除的内容。

示例2

输入:s = "deeedbbcccbdaa", k = 3
输出:"aa"
解释: 
先删除 "eee" 和 "ccc",得到 "ddbbbdaa"
再删除 "bbb",得到 "dddaa"
最后删除 "ddd",得到 "aa"

示例3

输入:s = "pbbcggttciiippooaais", k = 2
输出:"ps"

链接:

1209. 删除字符串中的所有相邻重复项 II - 力扣(LeetCode) (leetcode-cn.com)

解题思路 

思路一: 暴力解法

算法:

  1. 记录字符串的长度
  2. 遍历字符串:

        如果当前字符与前一个相同,计数器加 1;否则,重置计数器为 1

        如果计数器等于 k,删除这 k 个字符

    3. 如果字符串的长度被改变,从头开始重新遍历字符串

实现代码如下:

/**
 * @param {string} s
 * @param {number} k
 * @return {string}
 */
var removeDuplicates = function(s, k) {
  let res = s, len = -1;
  while (len != res.length) {
    len = res.length;
    for (let i = 0, count = 1; i < res.length; ++i) {
      if (i == 0 || res[i] != res[i - 1]) {
        count = 1;
      } else if (++count == k) { 
        res = res.slice(0, i - k + 1) + res.slice(i + 1)
        break;
      }
    }
  }
  return res;
 }

思路二:记忆计数 

从方法一中可以看出,如果为每个字符设置计数器,就不必每次删除完字符后从头开始。这种方法具有线性复杂度,但需要额外空间存储字符的计数器。

算法:

1. 初始数组 counts。

2. 遍历字符串:

        如果当前字符与上一个字符相等,令 counts[i] = counts[i - 1] + 1;否则,令 counts[i] = 1。

        如果 counts[i] = k,删除这 k 个字符,令 i = i - k。

实现代码如下:  

/**
 * @param {string} s
 * @param {number} k
 * @return {string}
 */
var removeDuplicates = function (s, k) {
  let res = s,
    counts = [];
  for (let i = 0; i < res.length; ++i) {
    if (i == 0 || res.charAt(i) != res.charAt(i - 1)) {
      counts[i] = 1;
    } else {
      counts[i] = counts[i - 1] + 1;
      if (counts[i] == k) {
        res = res.slice(0, i - k + 1) + res.slice(i + 1);
        i = i - k;
      }
    }
  }
  return res;
};

时间复杂度:O(n),其中 n 是字符串长度。每个字符仅被处理一次。

空间复杂度:O(n)

思路三:

「LeedCode 1047: 删除字符串中的所有相邻重复项」一样,只是现在每次入栈 k-1 个,栈头的 k-1 个元素的第一个如果与当前准备入栈的元素相同,则代表k个元素是一样的,则无需入栈

/**
 * @param {string} s
 * @param {number} k
 * @return {string}
 */
var removeDuplicates = function(s, k) {
  if(s.length <= 1) {
    return s;
  }
  const stack = [];
  for (const ch of s) {
    let prev = stack.pop();
    if (!prev || prev[0] !== ch) {
      stack.push(prev);
      stack.push(ch);
    } else if (prev.length < k - 1){
      stack.push(prev + k)
    }
  }
  return stack.join("");
};

时间复杂度: O(n),其中 n 是字符串的长度   

空间复杂度: O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值