问题描述
给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。
若可行,输出任意可行的结果。若不可行,返回空字符串。
示例 1:
输入: S = “aab”
输出: “aba”
示例 2:
输入: S = “aaab”
输出: “” 注意:
S 只包含小写字母并且长度在[1, 500]区间内。
解题思路
重构字符串时,需要根据每个字母在字符串中出现的次数处理每个字母放置的位置。如果出现次数最多的字母可以在重新排布之后不相邻,则可以重新排布字母使得相邻的字母都不相同。如果出现次数最多的字母过多,则无法重新排布字母使得相邻的字母都不相同。
假设字符串的长度为 n,如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现多少次?
当 n 是偶数时,有 n/2 个偶数下标和 n/2 个奇数下标,因此每个字母的出现次数都不能超过 n/2 次,否则出现次数最多的字母一定会出现相邻。
当 n是奇数时,由于共有 (n+1)/2 个偶数下标,因此每个字母的出现次数都不能超过 (n+1)/2 次,否则出现次数最多的字母一定会出现相邻。
由于当 n是偶数时,在整数除法下满足 n/2 和 (n+1)/2 相等,因此可以合并 n 是偶数与 n 是奇数的情况:如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现 (n+1)/2 次。
基于此,我们可以使用基于最大堆的贪心算法
维护最大堆存储字母,堆顶元素为出现次数最多的字母。首先统计每个字母的出现次数,然后将出现次数大于 0 的字母加入最大堆。
当最大堆的元素个数大于 1 时,每次从最大堆取出两个字母,拼接到重构的字符串,然后将两个字母的出现次数分别减 1,并将剩余出现次数大于 0 的字母重新加入最大堆。由于最大堆中的元素都是不同的,因此取出的两个字母一定也是不同的,将两个不同的字母拼接到重构的字符串,可以确保相邻的字母都不相同。
如果最大堆变成空,则已经完成字符串的重构。如果最大堆剩下 1 个元素,则取出最后一个字母,拼接到重构的字符串。
而最大堆的实现的数据结构是PriorityQueue.实现这个数据结构我们需要写一个比较器Comparator,比较器的具体实现如代码所述
代码实现
class Solution {
public String reorganizeString(String S) {
final int alpha[]=new int[26];
int len=S.length();
for(int i=0;i<len;i++){
char ch=S.charAt(i);
alpha[ch-'a']++;
}
//判断有没有可能有的字符出现超过(n+1)/2的次数
int maxOccur=alpha[0];
for(int i=0;i<alpha.length;i++){
if(alpha[i]>maxOccur){
maxOccur=alpha[i];
}
}
if(maxOccur>((len+1)/2)){
return "";
}
//使用优先级队列,队列的比较器为按照alpha的值从大到小进行排列,这里面参数没有数组,但其实用到了数组
PriorityQueue<Character> queue=new PriorityQueue<>(new Comparator<Character>(){
@Override
public int compare(Character o1, Character o2) {
return alpha[o2-'a']-alpha[o1-'a'];
}
});
//将元素一个一个的加入进去
for(int i=0;i<26;i++){
if(alpha[i]>0){
queue.offer((char) ('a'+i));
}
}
//使用StringBuilder,拼接效率更高
StringBuilder result=new StringBuilder();
/*这里为什么size()大于1,因为下面我们每次取出两个元素,如果size()大于0,当为奇数的时候,最终size等于1,无法取出两个元素*/
while(queue.size()>1){
/*
每次取出队列中两个出现次数最多的两个字符,然后添加到结果字符串中,并且更新alpha数组的值
最后如果这两个字符对应的alpha数组的值仍然大于1,就需要重新添加到队列中进行比较
*/
char ch1=queue.poll();
char ch2=queue.poll();
result.append(ch1);
result.append(ch2);
alpha[ch1-'a']--;
alpha[ch2-'a']--;
if(alpha[ch1-'a']>0){
queue.offer(ch1);
}
if(alpha[ch2-'a']>0){
queue.offer(ch2);
}
}
//可能while循环结束之后,因为奇数的缘故,最后队列中可能还有元素
if(queue.size()>0){
result.append(queue.poll());
}
return result.toString();
}
}