转载自:https://blog.csdn.net/haut_ykc/article/details/104570022/
给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。
若可行,输出任意可行的结果。若不可行,返回空字符串。
示例 1:
输入: S = "aab"
输出: "aba"
示例 2:
输入: S = "aaab"
输出: ""
注意:
S 只包含小写字母并且长度在[1, 500]区间内。
方法一:排序,我们按照字符出现的频率进行排序,这样相同的字符此时是连续出现的,我们可以每隔一个字符空位插入一个字符,但是有一种特殊情况是出现次数最多的字符可能出现了(n+1)/2次,此时有可能前两个字符会出现相同的情况,为了避免这种情况,我们要使出现次数最多的字母排在最后,因此我们选择从小到大排序,问题解决了。
class Solution {
public String reorganizeString(String S) {
//1、满足两两相邻的的字符不同,则出现频率最高的字符不能超过一半【length+1】/2
//1.1求出每个字母出现的频率['a'=>6] ---->[0=>6]
int[] nums = new int[26];
int len = S.length();
for(char c:S.toCharArray()){
nums[c-'a'] +=100;
}
for(int i=0;i<26;i++){
nums[i] +=i;
}
Arrays.parallelSort(nums);
int t=1;
char[] ans = new char[len];
for(int num:nums){
int ct = num/100;
char ch = (char)('a'+ (num%100));
if(ct>(len+1)/2) return "";
for(int i=0;i<ct;i++){
if(t>=len) t=0;
ans[t] = ch;
t += 2;
}
}
return new String(ans);
}
}
方法二:优先队列。我们考虑每次插入的字符是出现次数最多的字符,可以这么做的原因在于,只有当一个字符出现的次数超过 (N+1) / 2 的情况下,这个问题才无解。只要初试情况下这个条件满足,每次都输出剩余出现次数最多的字符就可以将这个条件一直保持下去。
class Solution {
class node{
int count;
char letter;
public node(int count,char letter) {
this.count=count;
this.letter=letter;
}
}
public String reorganizeString(String S) {
int len=S.length();
int[] nums=new int[26];
for(char c : S.toCharArray()) nums[c-'a']++;
PriorityQueue<node> q=new PriorityQueue<node>(
(a,b)->a.count==b.count?a.letter-b.letter:b.count-a.count);
for(int i=0;i<26;i++) {
if(nums[i]==0) continue;
if(nums[i]>(len+1)/2)
return "";
q.add(new node(nums[i],(char)('a'+i)));
}
StringBuilder ans=new StringBuilder();
while(q.size()>=2) {
node a1=q.poll();
node a2=q.poll();
ans.append(a1.letter);
ans.append(a2.letter);
if(--a1.count>0) q.add(a1);
if(--a2.count>0) q.add(a2);
}
if(q.size()>0) ans.append(q.poll().letter);
return new String(ans);
}
}