Given a string S, check if the letters can be rearranged so that two characters that are adjacent to each other are not the same.

If possible, output any possible result.  If not possible, return the empty string.

Example 1:

Input: S = "aab"
Output: "aba"

Example 2:

Input: S = "aaab"
Output: ""

Note:

  • S will consist of lowercase letters and have length in range [1, 500].

这道题给了一个字符串,让我们重构这个字符串,使得相同的字符不会相邻,如果无法做到,就返回空串,题目中的例子很好的说明了这一点。如果先不考虑代码实现,让你来手动重构的话,该怎么做呢?其实就是把相同的字符分开。比如例子1中,两个a相邻了,所以把第二个a和后面的b交换位置,这样分开了相同的字符,就是最终答案了。再来看一个例子,比如 "aaabbc",当发现第二个字符也是 ‘a’ 的时候,就需要往后遍历找到第一个不是 ‘a’ 的字符,即 ‘b’,然后交换 ‘a’ 和 ‘b’ 即可,然后继续往后面进行同样的处理,当无法找到不同的字符后就返回空串。这种方法对有序的字符串S是可以的,虽然题目给的两个例子中字符串S都是有序的,实际上不一定是有序的。所以博主最先的想法是给数组排序呗,但是博主的这个解法跪在了这个例子上 "vvvlo",我们发现排序后就变成 "lovvv",这样上面提到的解法就跪了。其实这里次数出现多的字符串需要在前面,这样才好交换嘛。那么还是要统计每个字符串出现的次数啊,这里使用 HashMap 来建立字母和其出现次数之间的映射。由于希望次数多的字符排前面,可以使用一个最大堆,C++ 中就是优先队列 Priority Queue,将次数当做排序的 key,把次数和其对应的字母组成一个 pair,放进最大堆中自动排序。这里其实有个剪枝的 trick,如果某个字母出现的频率大于总长度的一半了,那么必然会有两个相邻的字母出现。这里博主就不证明了,感觉有点像抽屉原理。所以在将映射对加入优先队列时,先判断下次数,超过总长度一半了的话直接返回空串就行了。

好,最大堆建立好以后,此时难道还是应该使用上面所说的交换的方法吗?其实直接构建新的字符串要更加简单一些。接下来,每次从优先队列中取队首的两个映射对儿处理,因为要拆开相同的字母,这两个映射对儿肯定是不同的字母,可以将其放在一起,之后需要将两个映射对儿中的次数自减1,如果还有多余的字母,即减1后的次数仍大于0的话,将其再放回最大堆。由于是两个两个取的,所以最后 while 循环退出后,有可能优先队列中还剩下了一个映射对儿,此时将其加入结果 res 即可。而且这个多余的映射对儿一定只有一个字母了,因为提前判断过各个字母的出现次数是否小于等于总长度的一半,按这种机制来取字母,不可能会剩下多余一个的相同的字母,参见代码如下:

解法一:

class Solution {
public:
string reorganizeString(string S) {
string res = "";
unordered_map<char, int> m;
priority_queue<pair<int, char>> q;
for (char c : S) ++m[c];
for (auto a : m) {
if (a.second > (S.size() + ) / ) return "";
q.push({a.second, a.first});
}
while (q.size() >= ) {
auto t1 = q.top(); q.pop();
auto t2 = q.top(); q.pop();
res.push_back(t1.second);
res.push_back(t2.second);
if (--t1.first > ) q.push(t1);
if (--t2.first > ) q.push(t2);
}
if (q.size() > ) res.push_back(q.top().second);
return res;
}
};

下面这种解法的原理和上面的很类似,就是写法上很秀,堪比陈独秀。这里使用了一个长度为 26 的一位数组 cnt 来代替上面的 HashMap 进行统计字母的出现次数,然后比较秀的一点是,把上面的映射对儿压缩成了一个整数,做法是将次数乘以了 100,再加上当前字母在一位数字中的位置坐标i,这样一个整数就同时 encode 了次数和对应字母的信息了,而且之后 decode 也很方便。数组 cnt 更新好了后,需要排个序,这一步就是模拟上面解法中最大堆的自动排序功能。不过这里是数字小的在前面,即先处理出现次数少的字母。这里除了和上面一样检测次数不能大于总长度的一半的操作外,还有一个小 trick,就是构建字符串的时候,是从第二个位置开始的。这里构建的字符串是直接对原字符串S进行修改的,因为 cnt 数组建立了之后,字符串S就没啥用了。用一个变量 idx 来表示当前更新字母的位置,初始化为1,表示要从第二个位置开始更新。因为出现次数最多的字母一定要占据第一个位置才行,这就是留出第一个位置的原因。这里很叼的一点,就是隔位更新,这样能保证相同的字母不相邻,而且当 idx 越界后,拉回到起始位置0,这就有点遍历循环数组的感觉。举个栗子来说吧,比如 "aaabbc",更新顺序为:

_ c _ _ _ _

_ c _ b _ _

_ c _ b _ b

a c _ b _ b

a c a b _ b

a c a b a b

解法二:

class Solution {
public:
string reorganizeString(string S) {
int n = S.size(), idx = ;
vector<int> cnt(, );
for (char c : S) cnt[c - 'a'] += ;
for (int i = ; i < ; ++i) cnt[i] += i;
sort(cnt.begin(), cnt.end());
for (int num : cnt) {
int t = num / ;
char ch = 'a' + (num % );
if (t > (n + ) / ) return "";
for (int i = ; i < t; ++i) {
if (idx >= n) idx = ;
S[idx] = ch;
idx += ;
}
}
return S;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/767

类似题目:

Rearrange String k Distance Apart

Task Scheduler

参考资料:

https://leetcode.com/problems/reorganize-string/

https://leetcode.com/problems/reorganize-string/discuss/113440/Java-solution-PriorityQueue

https://leetcode.com/problems/reorganize-string/discuss/113427/C%2B%2B-Greedy-sort-O(N)

https://leetcode.com/problems/reorganize-string/discuss/232469/Java-No-Sort-O(N)-0ms-beat-100

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Reorganize String 重构字符串的更多相关文章

  1. [LeetCode] 767. Reorganize String 重构字符串

    Given a string S, check if the letters can be rearranged so that two characters that are adjacent to ...

  2. [LeetCode] Decode String 解码字符串

    Given an encoded string, return it's decoded string. The encoding rule is: k[encoded_string], where ...

  3. [LeetCode] Scramble String 爬行字符串

    Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrin ...

  4. [LeetCode] Magical String 神奇字符串

    A magical string S consists of only '1' and '2' and obeys the following rules: The string S is magic ...

  5. [LeetCode] Rotate String 旋转字符串

    We are given two strings, A and B. A shift on A consists of taking string A and moving the leftmost ...

  6. LeetCode - Reorganize String

    Given a string S, check if the letters can be rearranged so that two characters that are adjacent to ...

  7. [LeetCode] Reverse String 翻转字符串

    Write a function that takes a string as input and returns the string reversed. Example: Given s = &q ...

  8. [leetcode]Weekly Contest 68 (767. Reorganize String&&769. Max Chunks To Make Sorted&&768. Max Chunks To Make Sorted II)

    766. Toeplitz Matrix 第一题不说,贼麻瓜,好久没以比赛的状态写题,这个题浪费了快40分钟,我真是...... 767. Reorganize String 就是给你一个字符串,能不 ...

  9. Leetcode 8. String to Integer (atoi) atoi函数实现 (字符串)

    Leetcode 8. String to Integer (atoi) atoi函数实现 (字符串) 题目描述 实现atoi函数,将一个字符串转化为数字 测试样例 Input: "42&q ...

随机推荐

  1. PHP-CGI,FASTcgi,php-fpm,之间的关系?

    刚开始对这个问题我也挺纠结的,看了<HTTP权威指南>后,感觉清晰了不少.首先,CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编写者.   ...

  2. NEO从入门到开窗(4) - NEO CLI

    一.唠叨两句 首先,我们都知道区块链是去中心化的,其中节点都是对等节点,每个节点都几乎有完整的区块链特性,CLI就是NEO的一个命令行对等节点,当然也有GUI这个项目,图形化的NEO节点.节点之间需要 ...

  3. 【R语言系列】R语言初识及安装

    一.R是什么 R语言是由新西兰奥克兰大学的Ross Ihaka和Robert Gentleman两个人共同发明. 其词法和语法分别源自Schema和S语言. R定义:一个能够自由幼小的用于统计计算和绘 ...

  4. 构造函数与析构函数(construction undergoing)

    构造函数和析构函数 一.构造函数: 1.普通构造函数:在对象被创建时利用特定的值构造对象,将对象初始化到一个特定的状态. 特性:构造函数的函数名和类名相同:没有返回值:在对象被创建时被自动调用:如果有 ...

  5. [福大软工] W班 软件产品案例分析

    作业要求 https://edu.cnblogs.com/campus/fzu/FZUSoftwareEngineering1715W/homework/1300 评分细则 第一部分 调研,评测 (3 ...

  6. bug终结者 团队作业第八周

    bug终结者 团队作业第八周 本次任务 素材提供及编辑:20162328 蔡文琛 博客修改完善:20162322 朱娅霖 "bug终结者" 宏伟蓝图 UML 手绘底稿 用例图 选项 ...

  7. backpropagation

    github: https://github.com/mattm/simple-neural-network blog: https://mattmazur.com/2015/03/17/a-step ...

  8. python 单向链表实现

    单链表的操作 is_empty() 链表是否为空 length() 链表长度 travel() 遍历整个链表 add(item) 链表头部添加元素 append(item) 链表尾部添加元素 inse ...

  9. 利用python实现简单随机验证码

    #!/usr/bin/env python # -*- coding:utf-8 -*- import random temp ='' for i in range(6): num = random. ...

  10. python 继承基础

    class annamal: def chi(self): print(self.name + '吃') def he(self): print(self.name + '喝') class dog( ...