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

332 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

LeetCode 1209 删除字符串中的所有相邻重复项 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"

解题思路 

思路一: 暴力解法

算法:

  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)

思路三:

遍历字符串依次入栈,入栈时,判断当前元素与栈头元素是否一致,如果不一致则入栈,如果一致,判断栈头字符是否长度为 k - 1 ,如果为 k-1 ,即加入该字符时,满足连续相同字符 k 个,此时,需要栈头出栈,当前字符不进栈,如果小于 k-1 ,则取出栈头字符,加上当前字符,重新入栈。

LeedCode 1047: 删除字符串中的所有相邻重复项 - 掘金 (juejin.cn)一样,只是现在每次入栈 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)

参考资料

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