题目
先看题目要求:
根据题目我们可以知道,若满足题目要求要返回一个新的字符串,不满足要求则返回一个空字符串。
解题思路
我们先要知道在什么情况下才满足题目要求,找到一个临界值,去判断是否要返回" "。
临界值就是在这个字符串中出现最多的字符
如果要使得两相邻的字符不同,那么出现次数最多的那个数的数量必须满足下面条件,如下图所示,比如下面的a是出现次数最多的
这个时候a的数量已经达到了临界值,如果再多一个 a ,那么至少有两个 a 是相邻的。所以这里出现次数最多的那个字符数量的临界值是threshold = (length + 1) >> 1(其中 length 是字符串的长度)
如果能使得两相邻的字符不同,我们可以先把出现次数最多的那个字符放到新字符串下标为偶数的位置上,放完之后在用其他的字符填充字符串剩下的位置。
在这里很多人会想为什么要放到偶数位上,而不放到奇数位上,我们来思考一下。
在数组中如果数组的长度为偶数的话,那奇数位与偶数位相同,那数组长度为奇数呢,是不是偶数位比奇数位多一个,如果放到了奇数位上就会出现这种情况:
传进来的字符串s:aaabb
将最多的放到奇数位:babaa
将最多的放到偶数位:ababa
这样对比就会很明显了
代码实现
第一步:先找出该字符串中哪个字符出现的次数最多
第二步:判断出现最多的字符是否超过了字符串长度的一半,超过返回空字符串,未超过则进行排列
第三步:对字符串进行重组,返回一个使得两相邻的字符不同的字符串
public static String reorganizeString(String s) {
char[] arr = s.toCharArray();
int[] count = new int[26];
int len = s.length();
for (int i = 0; i < len; i++) {
// 统计每个字符出现的次数
count[arr[i] - 'a']++;
}
// 如果最多的字符要大于整个字符串的一半的话就必然会有两个相临
int max = 0, mid = (len+1)>>1;
// 记录出现最多次数的下标,用于寻找是哪个字符
int alphabet = 0;
// 找出出现最多的字符
for (int i = 0; i < count.length; i++) {
if (count[i]>max){
max =count[i];
alphabet = i;
// 当出现最多的字符大于一半的话,就直接返回
if (max > mid){
return "";
}
}
}
/**
* 到达这里就说明可以组成一个两相邻字符不同的字符串
* 所以下一步就是返回一个两不相邻的字符串res
* 整体思想就是把出现最多的字符串放到偶数位上,在这里面不放在奇数位上,是因为数组里面奇数长度的奇数会比偶数少一个
*/
char[] res = new char[len];
// 在这里维护一个index,用于填入字符
int index = 0;
while(count[alphabet]-- > 0){
res[index] = (char)(alphabet+'a');
index += 2;
}
// 把剩余的字符放到空位上
for (int i = 0; i < count.length; i++) {
while(count[i]-- > 0){
// 如果到达了res的尽头,就说明偶数已经排完了,就需要从奇数开始进行填充
if (index >= res.length){
index = 1;
}
res[index] = (char)(i + 'a');
index += 2;
}
}
return new String(res);
}
最后大家可以思考一下当初始字符串为aaabbbccc时会返回什么结果