767. 重构字符串
Desciption
给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。 若可行,输出任意可行的结果。若不可行,返回空字符串。 示例:
输入: S = "aab"
输出: "aba"
输入: S = "aaab"
输出: ""
注意: S 只包含小写字母并且长度在[1, 500]区间内
Solution
贪心,构造
假设字符串的长度为n,只要某个字符出现次数超过 (n + 1) / 2,则输出"";想了一下,剩下的情况是一定能够构造出结果的
接下来考虑一下怎么构造,想到一种构造方案
1. 统计每个字符出现次数并放进优先队列,按次数从大到小排序2. 先构造奇数位置的字符,从优先队列取出出现次数最多的字符,在该奇数位放入该字符后,字符次数减一并重新push进队列,一直构造 (n + 1) / 2 个位置3. 按照第2步的方法继续构造偶数位上面的方法有反例:vvvlo,当第1、3两个字符为v之后,第5个字符队列里取出来的不确定,会导致第三个v会和前面两个v相邻。考虑一种更为简单的构造:
- 取出出现次数最多的字符,然后从第0个位置,把它隔着一位全部放完,接下来再找出出现次数最多的字符,接着上次的地方继续放
- 当长度超过n之后,从第1个位置开始放,直到放完所有字符,就是一个可行的答案
Code
public String reorganizeString(String S) {
int n = S.length();
int[] countArr = new int[26];
Arrays.fill(countArr, 0);
for (int i = 0; i < n; ++i) {
int val = S.charAt(i) - 'a';
countArr[val]++;
if (countArr[val] > (n + 1) / 2) {
return "";
}
}
char[] res = new char[n];
int len = 0, cur = 0;
while (len < n) {
int pos = 0;
for (int i = 0; i < 26; ++i) {
if (countArr[i] > countArr[pos]) {
pos = i;
}
}
for (int i = 0; i < countArr[pos]; ++i) {
res[cur] = (char)(pos + 'a');
cur += 2;
if (cur >= n) {
cur = 1;
}
}
len += countArr[pos];
countArr[pos] = 0;
}
return String.valueOf(res);
}