在问 · 算法题解

767. 重构字符串

中等栈与堆贪心 + 大根堆力扣原题 ↗

题目描述

输入字符串 s。把其中的字符重新排列,使得任意两个相邻位置上的字符都不相同。

如果可以做到,返回任意一种满足要求的排列;如果做不到,返回空字符串 ""。

示例 1
输入:s = "aab"
输出:"aba"

解释:两个 a 被 b 隔开即可。

示例 2
输入:s = "aaab"
输出:""

解释:a 有 3 个而其他字符只有 1 个,无论怎样排都有两个 a 相邻。

数据范围

  • 1 <= s.length <= 500
  • s 只含小写字母。

解题思路:贪心 + 大根堆

贪心:每一步都放“剩余次数最多、且不等于上一个字符”的字母。用最大堆按剩余次数取字母;刚放下的字母先不放回堆,而是存在 prev 里“冷却一轮”,下一轮取出新字母后再把 prev 放回堆。这样相邻两个字母一定不同。如果堆空了 prev 还有剩余,说明只剩这一种字母且和末尾相同,无解。

为什么正确

res 中相邻字符互不相同;prev 是 res 最后一个字符(如果它还有剩余),它暂时不在堆中。

复杂度分析

时间 O(n log k),k 为字母种类(≤ 26);空间 O(k)。

易错点

判断无解的条件也可以提前算:最多的字母次数超过 (n+1)/2 就无解。答案不唯一,只要相邻不同即可。别把刚取出的字母立刻放回堆,否则下一轮可能又取到它。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def reorganizeString(self, s: str) -> str:
        cnt = collections.Counter(s)
        heap = [(-c, ch) for ch, c in cnt.items()]
        heapq.heapify(heap)
        res = []
        prev = None
        while heap:
            c, ch = heapq.heappop(heap)
            res.append(ch)
            if prev:
                heapq.heappush(heap, prev)
            prev = (c + 1, ch) if c + 1 < 0 else None
        return '' if prev else ''.join(res)

载入编辑器,自己跑一遍 →

其他解法

堆的做法每步选剩余最多的字母,可推广到“相同字母至少隔 k 个”的问题;奇偶填充更快更短,但只适用于相邻不同这一情形。

解法 2:计数 + 奇偶下标填充 时间 O(n + k),空间 O(k)

统计每个字母的次数。若最多的字母出现次数超过 (n + 1) / 2,无解。否则先把最多的字母依次放在下标 0、2、4…,再把其余字母接着放在剩余的偶数下标,偶数下标用完后从 1 开始放奇数下标。同一字母在这个顺序里是连续放的,且次数不超过一半,所以它们在字符串中都隔开至少一个位置。

不需要堆,线性时间完成。先放最多的字母是关键,否则它可能从偶数下标末尾绕到奇数下标开头时与自己相邻,正确性需要稍加证明。

class Solution:
    def reorganizeString(self, s: str) -> str:
        n = len(s)
        cnt = [0] * 26
        for ch in s:
            cnt[ord(ch) - 97] += 1
        top = max(range(26), key=lambda c: cnt[c])
        if cnt[top] > (n + 1) // 2:
            return ''
        res = [''] * n
        idx = 0
        order = [top] + [c for c in range(26) if c != top]
        for c in order:
            for _ in range(cnt[c]):
                if idx >= n:
                    idx = 1
                res[idx] = chr(97 + c)
                idx += 2
        return ''.join(res)

栈与堆 专题的其他题目

查看 栈与堆 全部题目 →