题目:
给你一个非空的字符串 s 和一个整数 k,你要将这个字符串中的字母进行重新排列,使得重排后的字符串中相同字母的位置间隔距离至少为 k。
所有输入的字符串都由小写字母组成,如果找不到距离至少为 k 的重排结果,请返回一个空字符串 ""。
示例 1:
输入: s = "aabbcc", k = 3
输出: "abcabc"
解释: 相同的字母在新的字符串中间隔至少 3 个单位距离。
示例 2:
输入: s = "aaabc", k = 3
输出: ""
解释: 没有办法找到可能的重排结果。
示例 3:
输入: s = "aaadbbcc", k = 2
输出: "abacabcd"
解释: 相同的字母在新的字符串中间隔至少 2 个单位距离。
解答:
贪心法,每次尽量取频次大的字母加入结果。
先统计每个字母出现的次数,存入map
然后把所有的[字母:频次]存入最大堆
每次从最大堆中取k个最大频次的字母,加入结果字符串。然后相应频次减一后再放回最大堆。
最终要么堆为空,则成功。如果堆中剩下不到k个字母,那么只能取一次(即字符串结尾,因为后面没有其他元素了),否则失败。
public static String getResult(String s,int k){
if(k<2)return s;
//记录小写字母出现的次数
int[] alph = new int[26];
for(char c:s.toCharArray())
alph[c-'a']++;
//构建大顶堆,根据字母出现次数的大小的顺序排序,且相同次数以字母表排序,并添加
PriorityQueue<Character> pq = new PriorityQueue<>((a,b)->{
if(alph[a-'a']!=alph[b-'a'])return Integer.compare(alph[b-'a'],alph[a-'a']);
return Character.compare(a,b);
});
for(char z ='a';z<='z';z++){
if(alph[z-'a']>0){
pq.add(z);
}
}
StringBuffer sb = new StringBuffer();
List<Character> list = new ArrayList<>();
int len = s.length();
while(!pq.isEmpty()){
//计算每一块的长度,避免最后剩余部分的长度小于k
k=Math.min(k,len);
for(int i =0;i<k;i++){
if(!pq.isEmpty()){
char c = pq.poll();
sb.append(c);
len--;
alph[c-'a']--;
//记录弹出的次数不为0的字母
if(alph[c-'a']>0)
list.add(c);
}else return "";
}
//将不为零的字母加入到大顶堆中
pq.addAll(list);
list.clear();
}
return sb.toString();
}