leetCode - 767. 重构字符串 及 优先队列的应用

本文探讨了如何使用Python和C++实现高效的字符串重组算法,针对最多字符的组合问题,介绍了两种方法:一种是使用heapq优先队列,另一种是根据字符频率直接填充。还提供了两种不同策略,以解决当最多字符数量超过半数时的优化问题。
摘要由CSDN通过智能技术生成

其中不太聪明的办法是每次都把最多的符号和次高的符号两两组合进新的数组。

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值