leetcode 做题记录 - 767.重构字符串

本文讲述了如何通过贪心策略和Map数据结构解决字符串重构问题,涉及字符计数、频率排序及优先队列技巧。博主逐步解析思路转变,从初始的复杂解法到最终使用Java PriorityQueue优化。

原题

767. 重构字符串

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:

输入: S = "aab"
输出: "aba"
示例 2:

输入: S = "aaab"
输出: ""

注意:

  • S 只包含小写字母并且长度在[1, 500]区间内。

完整解题思路

初看该题时闹钟响起一声惊叹:卧槽,完蛋了,啥玩意儿,又不会了。然而我认为不能怎么轻易的就放弃,不然天天看题解,也甚是无趣… 遂思考了一阵,在 vscode 中写起了草稿,脑中有了个思路。

  1. 发现可以循环计算,使用 Map 存储键值对,计算每个字符出现的次数。
  2. 当出现最多的字符次数超过整体字符串长度半数 + 1 时,即不可能不出现相邻两字符不同的情况。

抓住这两点以后我以为自己已经解答出了这题,但是代码写起来看上去挺长的。嗯,这肯定是个垃圾解法,正确解法肯定代码一点点就搞定了,后续再随意拼一种结果出来就好了。

但是,当我写完上面两点逻辑之后,在拼后续结果的时候发现,妈呀,咋这么难拼… 然后陷入了沉思…

一会儿之后又有了思路:

  1. 先将整个 Map 内的 key 按照出现次数排序。嗯,看看有啥 Map 排序方法… 怎么一个 Map 排序方法都没有… 然后自己写了个简单的选择排序,获得了一个排好序的 key 数组。
  2. 排好序以后是不是从大到小,每个 key 各取一个即可获取到?尝试下… OK,提交报错,重来…
  3. 嗯… 那是不是每次拿两个,从大到小分别依次取最前面的两个就好了呢?试试,提交报错…此时一个小时过去… 我太菜了… 看看题解吧。
  4. 看完 Java 题解以后发现,原来题解也写了这么多代码… 诶,和我的思路就差一点点了,原来差的是每次取完两个都得排序一次,确保每次在头部的两个都是最多的,那得多耗费性能啊,时间复杂度肯定很高…答案竟然直接使用了 Java 的 PriorityQueue,无耻… 怪不得没有 JavaScript 解法!!!
  5. 提出排序方法,每次取完都排序一次,提交解决。OK,性能果然奇差无比,我真菜- -

总结

这次靠着自己的思考接近了终点,但是没有到达终点便是失败… 而且题解说了是贪心算法,我也没发现,自己回去再翻翻书看看贪心算法吧!

另一种解法今天暂时不看了,感觉脑细胞死的差不多了。

正经题解

  1. 先将每个字符的出现的次数存入 Map(题解是将其分为26个字母,每个字母占一个下标来存储出现次数)
  2. 获取出现最多次数的字符,判断其是否小于等于总长度的 (1/2 + 1),满足则表示有可能的组合,反之则没有
  3. 按照 Key 值出现的次数排序,循环每次获取最前面两个字符(题解是用 Java,直接使用 PriorityQueue,最优先队列,自定义排序规则,每次从队列中取出以后会自动排序,最前面的永远都是次数最多的)
  4. 每次获取完两个字符以后都重新排序,确保前面的两个字符都是当前 Map 中存在次数最多的
  5. 循环2个2个字符获取,直到取完所有字符

JavaScript 解法

var sort = (sortedKey, map) => {
  for (let index = 0; index < sortedKey.length - 1; index++) {
    for (let innerIndex = index + 1; innerIndex < sortedKey.length; innerIndex++) {
      if (map.get(sortedKey[index]) < map.get(sortedKey[innerIndex])) {
        let temp = sortedKey[index];
        sortedKey[index] = sortedKey[innerIndex];
        sortedKey[innerIndex] = temp;
      }
    }
  }

  return sortedKey;
}
/**
 * @param {string} S
 * @return {string}
 */
var reorganizeString = function(S) {
  let result = '';

  const map = new Map();
  const splitedStr = S.split('');
  for (let s of splitedStr) {
    map.set(s, (map.get(s) || 0) + 1);
  }

  if (map.size > 0) {
    let sortedKey = sort([...map.keys()], map);

    let maxCount = map.get(sortedKey[0]);
    if (Math.ceil(splitedStr.length / 2) >= maxCount) {
      let end = false;
      while (!end) {
        let temp = '';
          for (const key of sortedKey) {
            if (map.get(key) > 0) {
              temp += key;
              map.set(key, map.get(key) - 1);
            }

            if (temp.length === 2) {
              break;
            }
          }
          if (temp.length === 0) {
            end = true;
          }

          sortedKey = sort(sortedKey, map);
          result += temp;
      }
    }
  }

  return result;
};

声明

题目来源于力扣,侵删。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值