letcode每日一題-重構字串
阿新 • • 發佈:2020-11-30
週末去了動物園,雖然我幾乎被凍成了狗,但還是好開心呀,今天開開心心的來更新啦!!!
題目描述:
方法:基於最大堆的貪心演算法
維護最大堆儲存字母,堆頂元素為出現次數最多的字母。首先統計每個字母的出現次數,然後將出現次數大於 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 ""; }