題目

https://leetcode.com/problems/reorganize-string/

解法

鴿巢原理經典題。 有 S 個格子,要求相鄰元素互相不同,則同一種元素至多可以放 ceil(S/2) 個。
本題相當於 N 個格子,重新填入所有元素,且必須滿足相鄰不同。


設元素個數最多的為 MX,若 MX 不超過 ceil(S/2) 則保證可以相鄰不同。
依照元素個數遞減排序後,從最多的開始放。
先放偶數格子,放完再放奇數格子。

時間複雜度 O(N + D log D),其中 D = 不同元素個數,本題為 26。
空間複雜度 O(N + D)。

class Solution:
    def reorganizeString(self, s: str) -> str:
        cnt = Counter(s)
        pairs = cnt.most_common()  # 按照出現次數遞減排序

        S = len(s)  # 總個數
        MX = pairs[0][1]  # 最大出現次數

        # 最大的超過一半
        # if MX > (S + 1) // 2:
        if MX > (S - MX) + 1:
            return ""

        ans = [None] * S
        i = 0  # 先填偶數位
        for k, v in pairs:
            for _ in range(v):
                ans[i] = k
                i += 2
                if i >= S:  # 偶數填完,填奇數位
                    i = 1

        return "".join(ans)

也可以按照剩餘次數裝進 max heap,每次拿出剩餘最多的兩種元素出來判斷:
若剩最多和前一個元素不同,就放;如果相同,就考慮第二多的;如果沒第二多的就代表不合法。

時間複雜度 O(N + D log D),其中 D = 不同元素個數,本題為 26。
空間複雜度 O(D)。

class Solution:
    def reorganizeString(self, s: str) -> str:
        h = []
        d = Counter(s)
        for k, v in d.items():
            heappush(h, [-v, k])

        ans = []
        last = ""
        while h:
            t = heappop(h)
            cnt, c = -t[0], t[1]
            # use max
            if c != last:
                last = c
                ans.append(c)
                if cnt - 1 > 0:  # check if put mx back
                    heappush(h, [-(cnt-1), c])
                continue

            # use max2
            if h:
                t = heappop(h)
                cnt2, c2 = -t[0], t[1]
                last = c2
                ans.append(c2)
                heappush(h, [-(cnt), c])  # always put mx back
                if cnt2 - 1 > 0:  # check if put mx2 back
                    heappush(h, [-(cnt2-1), c2])
                continue

            # invalid
            return ""

        return "".join(ans)