【LeetCode】358.K 距離間隔重排字符串

358.K 距離間隔重排字符串

知識點:哈希表;貪心;堆;隊列

題目描述

給你一個非空的字符串 s 和一個整數 k,你要將這個字符串中的字母進行重新排列,使得重排後的字符串中相同字母的位置間隔距離至少為 k。

所有輸入的字符串都由小寫字母組成,如果找不到距離至少為 k 的重排結果,請返回一個空字符串 「」
說明:你不能傾斜容器。

示例

image

示例 1:
輸入: s = "aabbcc", k = 3
輸出: "abcabc" 
解釋: 相同的字母在新的字符串中間隔至少 3 個單位距離。

示例 2:
輸入: s = "aaabc", k = 3
輸出: "" 
解釋: 沒有辦法找到可能的重排結果。

示例 3
輸入: s = "aaadbbcc", k = 2
輸出: "abacabcd"
解釋: 相同的字母在新的字符串中間隔至少 2 個單位距離。


解法一:貪心+隊列

這道題應該先對每個字符統計數字,然後應該先插入的是數字最多的字符,這時候需要把這個字符的數量-1,而且應該先把這個字符移出去,因為下一次不能插入它了,必須等長度到達k後才能插入它。
對於插入數字最多的字符,應該排序,所以可以用一個大根堆,堆頂就是數量最多的。
而對於把插入的移出去,可以用隊列。
判斷隊列中的元素個數是否為k,if是的話,那說明對於插入的字符c,它已經間隔夠k個字符了,所以c可以再出去一次了,那麼久可以把c這個隊列頭拿出來,放到大根堆里去了。
當大根堆沒有元素後,可以判斷res的長度,if等於原來的長度,那證明重構完成了,if不等於,那說明有字符還在queue里,重構失敗;

class Solution {
    public String rearrangeString(String s, int k) {
        if (k <= 1) {
            return s;
        }
        HashMap<Character, Integer> map = new HashMap<>();
        // 大頂堆
        PriorityQueue<Map.Entry<Character, Integer>> maxHeap = new PriorityQueue<>((a, b) -> b.getValue() - a.getValue());
        for (Character c : s.toCharArray()) {
            // 遍歷字符,統計字符的出現次數
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        maxHeap.addAll(map.entrySet()); // 裝入大頂堆,按照字符重複次數作為比較
        StringBuilder sb = new StringBuilder(s.length());
        Queue<Map.Entry<Character, Integer>> queue = new LinkedList<>();
        while (!maxHeap.isEmpty()) {
            Map.Entry<Character, Integer> currentEntry = maxHeap.poll();    // 從大頂堆取出重複次數最多的字符
            sb.append(currentEntry.getKey());
            currentEntry.setValue(currentEntry.getValue() - 1); // 用掉一個字符,次數減一
            queue.offer(currentEntry);  // 放入到queue中,因為k距離後還要用。
            if (queue.size() == k) {
                // queue的大小到達了k,也就是說我們已經越過了k個單位,在結果中應該要出現相同的字母了
                Map.Entry<Character, Integer> entry = queue.poll();
                if (entry.getValue() > 0) {
                    // 該字符的重複次數大於 0,則添加入大頂堆中,要是0那還加它幹嘛
                    maxHeap.add(entry);
                }
            }
        }
        // 退出 while 循環就是大頂堆已經沒有元素了,如果此時 sb 的長度沒有還原,說明還有元素掛在 queue 中
        // 即 queue.size() == k 這個條件沒有完全滿足,即存在一些字符無法間隔 k 個距離
        return sb.length() == s.length() ? sb.toString() : "";
    }
}

  • python
import heapq
from collections import Counter, deque
class Solution:
    def combine_chars(self, input: str, interval: int) -> str:
        if interval <= 1:
            return input
        count = Counter(input)   #生成一個字典
        heap = [(-v, k) for k, v in count.items()]  #按照元祖的第一個即-v來生成小根堆,也就是v的大根堆
        heapq.heapify(heap)  #生成大根堆

        queue = deque()  # 隊列用來記錄已經被記錄的字符
        res = []
        while heap:
            count, ch = heapq.heappop(heap)
            res.append(ch)
            queue.append((count+1, ch))
            if len(queue) == interval:
                element = queue.popleft()
                if element[0] < 0:
                    heapq.heappush(heap, element)
        return "".join(res) if len(res) == len(input) else ""