其中不太聪明的办法是每次都把最多的符号和次高的符号两两组合进新的数组。
python代码:
import collections
import heapq
def reorganizeString(S):
if len(S) < 2:
return S
length = len(S)
counts = collections.Counter(S)
maxCount = max(counts.items(), key=lambda x: x[1])[1]
print(counts.items())
if maxCount > (length + 1) // 2:
return ""
queue = [(-x[1], x[0]) for x in counts.items()]
heapq.heapify(queue)
ans = list()
while len(queue) > 1:
_, letter1 = heapq.heappop(queue)
_, letter2 = heapq.heappop(queue)
ans.extend([letter1, letter2])
counts[letter1] -= 1
counts[letter2] -= 1
if counts[letter1] > 0:
heapq.heappush(queue, (-counts[letter1], letter1))
if counts[letter2] > 0:
heapq.heappush(queue, (-counts[letter2], letter2))
if queue:
ans.append(queue[0][1])
return "".join(ans)
collection模块的count方法可以进行统计string对象里每个字母(如对“aaabb“,有’a’ 3, ‘b’ 2)
python里的优先队列是heapq,堆。heapq堆只能是小根堆,头部永远是最小)所以此处要找最大值就要用负值。
以下为官方文档:
这个模块提供了堆队列算法的实现,也称为优先队列算法。
堆是一个二叉树,它的每个父节点的值都只会小于或大于所有孩子节点(的值)。它使用了数组来实现:从零开始计数,对于所有的 k ,都有 heap[k] <= heap[2k+1] 和 heap[k] <= heap[2k+2]。 为了便于比较,不存在的元素被认为是无限大。 堆最有趣的特性在于最小的元素总是在根结点:heap[0]。
这个API与教材的堆算法实现有所不同,具体区别有两方面:(a)我们使用了从零开始的索引。这使得节点和其孩子节点索引之间的关系不太直观但更加适合,因为 Python 使用从零开始的索引。 (b)我们的 pop 方法返回最小的项而不是最大的项(这在教材中称为“最小堆”;而“最大堆”在教材中更为常见,因为它更适用于原地排序)。
基于这两方面,把堆看作原生的Python list也没什么奇怪的: heap[0] 表示最小的元素,同时 heap.sort() 维护了堆的不变性!
要创建一个堆,可以使用list来初始化为 [] ,或者你可以通过一个函数 heapify() ,来把一个list转换成堆。
C++代码:
string reorganizeString(string S) {
if (S.length() < 2) {
return S;
}
vector<int> counts(26, 0);
int maxCount = 0;
int length = S.length();
for (int i = 0; i < length; i++) {
char c = S[i];
counts[c - 'a']++;
maxCount = max(maxCount, counts[c - 'a']);
}
if (maxCount > (length + 1) / 2) {
return "";
}
auto cmp = [&](const char& letter1, const char& letter2) {
return counts[letter1 - 'a'] < counts[letter2 - 'a'];
};
priority_queue<char, vector<char>, decltype(cmp)> queue{ cmp };
for (char c = 'a'; c <= 'z'; c++) {
if (counts[c - 'a'] > 0) {
queue.push(c);
}
}
string sb = "";
while (queue.size() > 1) {
char letter1 = queue.top(); queue.pop();
char letter2 = queue.top(); queue.pop();
sb += letter1;
sb += letter2;
int index1 = letter1 - 'a', index2 = letter2 - 'a';
counts[index1]--;
counts[index2]--;
if (counts[index1] > 0) {
queue.push(letter1);
}
if (counts[index2] > 0) {
queue.push(letter2);
}
}
if (queue.size() > 0) {
sb += queue.top();
}
return sb;
}
其中这段代码值得学习
auto cmp = [&](const char& letter1, const char& letter2) {
return counts[letter1 - 'a'] < counts[letter2 - 'a'];
};
priority_queue<char, vector<char>, decltype(cmp)> queue{ cmp };
通过lambda表达式定义了cmp比较的方式,然后调用stl的priority_queue来储存
其中声明格式如下:
priority_queue<char, vector<char>, decltype(cmp)> queue{ cmp };
三个参数,第一个为队列里元素的类型,实现的方法(vector<char>里实现),以及比较的方式(cmp),具体实例也应在{}中再次声明比较方式。
其实更为机智的办法就是,如果最多的字母数量不超过(1+n)/2,那么就可以写成正经形式。首先把最多的字母全放在偶数位置上,从0开始隔着一个写,直到写完。再把剩下的字母优先填满偶数位,再填奇数位。即可。
string reorganizeString(string S) {
if (S.length() < 2) {
return S;
}
vector<int> counts(26, 0);
int maxCount = 0 , maxIndex ;
int length = S.length();
for (int i = 0; i < length; i++) {
char c = S[i];
counts[c - 'a']++;
if (counts[c - 'a'] > maxCount) {
maxCount = counts[c - 'a'];
maxIndex = c - 'a';
}
}
if (maxCount > (length + 1) / 2)
return "";
int oddIndex = 1 , evenIndex = 0;
string ans(length, ' ');
while (counts[maxIndex] > 0){
ans[evenIndex] = 'a' + maxIndex;
counts[maxIndex]--;
evenIndex += 2;
}
for (size_t i = 0; i < 26 ; i++)
{
while (counts[i] > 0 && evenIndex < length)
{
counts[i]--;
ans[evenIndex] = ('a' + i);
evenIndex += 2;
}
while (counts[i] > 0 && oddIndex < length)
{
counts[i]--;
ans[oddIndex] = ('a' + i);
oddIndex += 2;
}
}
return ans;
}