目录
一、题目描述
给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。
若可行,输出任意可行的结果。若不可行,返回空字符串。
示例 1:
输入: S = "aab"
输出: "aba"
示例 2:
输入: S = "aaab"
输出: ""
注意:
- S 只包含小写字母并且长度在[1, 500]区间内。
二、解题思路
可以先直观经验考虑这道题,应该能想到插空法,就是在两个相同的字符之间插入一个不同的字符。那就会想到统计每种字符的出现次数,然后给他们安排位置,基本上这道题的大概思路就有了。
贪心插空,注意需要按照字符出现的次数考虑放在奇数还是偶数下标上。
- 如果字母的出现次数大于 0且小于或等于 n/2,且 oddIndex 没有超出数组下标范围,则将字母放置在 oddIndex,然后将 oddIndex 的值加 2。
- 如果字母的出现次数大于 n/2,或 oddIndex 超出数组下标范围,则将字母放置在 evenIndex,然后将 evenIndex 的值加 2。
可以使用反证法,证明当一个字符先出现在奇数位上然后在出现在偶数位上时,不会出现相邻的情况。
由于先出现在奇数位上,那么个数小于或等于 n/2,一旦相邻则长度至少为(n+1)/2,相互矛盾。
另外在题解区看见一位大佬的另一种更为直接的思路:按照字符出现的次数先从小到大排序,排序之后从次数少的字符开始直接往奇数位置上放,奇数位置放满了之后再往偶数位上放,这样可以不用在循环里做额外的判断就能保证次数等于 (n+1)/2的字符会出现在偶数位上。
三、代码实现
#include<bits/stdc++.h>
using namespace std;
string reorganizeString(string S) {
int n = S.size();
if (n < 2) return S;
vector<int> chvec(26, 0);
int maxCnt = 0;
for (int i = 0; i < n; ++i) {
chvec[S[i] - 'a']++;
maxCnt = max(maxCnt, chvec[S[i] - 'a']);
}
if (maxCnt > (n + 1) / 2) return "";
string res(n, ' ');
int evenIndex = 0, oddIndex = 1;
int halfNums = n / 2;
//对字符进行重排
for (int i = 0; i < 26; ++i) {
//出现次数小于长度一半并且奇数下标未超出范围,就放在奇数下标这种
//这里要先写奇数下标的循环判断,先写偶数有问题
while (chvec[i] > 0 && chvec[i] <= halfNums && oddIndex < n) {
res[oddIndex] = 'a' + i;
oddIndex += 2;
chvec[i]--;
}
//如果长度等于一半或者一半加一,或者奇数下标已经用完了,就用偶数下标
while (chvec[i] > 0) {
res[evenIndex] = 'a' + i;
evenIndex += 2;
chvec[i]--;
}
}
return res;
}
//这个思路好操作一些:按照出现次数从小到大排序之后,只要依次往奇数下标上放就行,奇数用完了再用偶数下标
//由于统计字符次数的数组会被排序打乱,所以要把字符和出现次数信息都记录在数组中
//这个数组很妙啊
string reorganizeString1(string S) {
int n = S.size();
vector<int> count(26, 0);
for (auto c : S) {
count[c - 'a'] += 100; // 统计次数
if (count[c - 'a'] / 100 > (n + 1) / 2) return ""; // 超过(n+1)/2个字符,不可能得到答案
}
for (int i = 0; i < 26; ++i) count[i] += i; // 将字符信息加入数组值中
// 此时count中的元素存储了次数和字符信息
// 如 count[i] = 203 表示字符 'd'='a'+203%100 出现了 2=203/100 次
sort(count.begin(), count.end()); // 对次数从小到大排序
int index = 1;
string ret(n, ' ');
for (auto c : count) {
int cnt = c / 100; // 取出次数
char ch = 'a' + (c % 100); // 取出字符信息
for (int i = 0; i < cnt; ++i) {
if (index >= n) index = 0; // 第一遍走完 从0开始
ret[index] = ch;
index += 2; // 间隔添加相同字符
}
}
return ret;
}
int main() {
string s = "ababababbaaabbcccdef";
cout << reorganizeString(s);
return 0;
}