letcode每日一题-重构字符串
周末去了动物园,虽然我几乎被冻成了狗,但还是好开心呀,今天开开心心的来更新啦!!!
题目描述:
方法:基于最大堆的贪心算法
维护最大堆存储字母,堆顶元素为出现次数最多的字母。首先统计每个字母的出现次数,然后将出现次数大于 00 的字母加入最大堆。
当最大堆的元素个数大于 11 时,每次从最大堆取出两个字母,拼接到重构的字符串,然后将两个字母的出现次数分别减 11,并将剩余出现次数大于 00 的字母重新加入最大堆。由于最大堆中的元素都是不同的,因此取出的两个字母一定也是不同的,将两个不同的字母拼接到重构的字符串,可以确保相邻的字母都不相同。
如果最大堆变成空,则已经完成字符串的重构。如果最大堆剩下 11 个元素,则取出最后一个字母,拼接到重构的字符串。
对于长度为 nn 的字符串,共有 n/2n/2 次每次从最大堆取出两个字母的操作,当 nn 是奇数时,还有一次从最大堆取出一个字母的操作,因此重构的字符串的长度一定是 nn。
当 nn 是奇数时,是否可能出现重构的字符串的最后两个字母相同的情况?如果最后一个字母在整个字符串中至少出现了 22 次,则在最后一次从最大堆取出两个字母时,该字母会先被选出,因此不会成为重构的字符串的倒数第二个字母,也不可能出现重构的字符串最后两个字母相同的情况。
因此,在重构字符串可行的情况下,基于最大堆的贪心算法可以确保得到正确答案。
实现代码如下:
public String reorganizeString(String S) {
int[] charCount=new int[26];
//统计字符出现的次数
for(int i=0;i<S.length();i++){
int index=S.charAt(i)-'a';
charCount[index]++;
}
//创建一个从大到小排序的队列
PriorityQueue<Character> queue=new PriorityQueue<>((o1,o2)->{
if(charCount[o1-'a']>charCount[o2-'a']){
return -1;
}
if(charCount[o1-'a']<charCount[o2-'a']){
return 1;
}
return 0;
});
//把字符放进队列中
for(int i=0;i<charCount.length;i++){
if(charCount[i]!=0){
queue.offer((char)(i+'a'));
}
}
StringBuilder result=new StringBuilder();
while (queue.size()>1){
//出现频率最高的字符
char max_count_char=queue.poll();
//出现频率第二高的字符
char second_count_char=queue.poll();
int maxIndex=max_count_char-'a';
int secondIndex=second_count_char-'a';
//重新构建字符串
if(result.length()==0 || result.charAt(result.length()-1)!=max_count_char){
result.append(max_count_char);
charCount[maxIndex]--;
}
int minLen=Math.min(charCount[maxIndex],charCount[secondIndex]);
for(int i=0;i<minLen;i++){
result.append(second_count_char).append(max_count_char);
}
//--重新构建字符串结束
charCount[maxIndex]-=minLen;
charCount[secondIndex]-=minLen;
if(charCount[maxIndex]>0){
queue.offer(max_count_char);
}
if (charCount[secondIndex]>0){
queue.offer(second_count_char);
}
}
if(queue.size()==0){
return result.toString();
}
if(queue.size()==1){
char last=queue.poll();
if(charCount[last-'a']==1 && result.charAt(result.length()-1)!=last){
result.append(last);
return result.toString();
}
}
return "";
}