【Leetcode】767. Reorganize String

記錄演算法發表於2020-10-30

題目地址:

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

給定一個長為 l l l的字串 s s s,只含小寫英文字母。將其字母重排,使得相鄰的字母不同,返回重排後的字串。如果不存在這樣的合法重排,則返回空串。

直覺上來講,只需要將出現最多次數的字元隔一個位置放,然後將別的字母插在中間即可。我們證明一個結論:存在那樣的合法重排當且僅當出現最多的字母出現次數小於等於 l / 2 l/2 l/2(當 l l l是偶數)或者小於等於 l / 2 + 1 l/2+1 l/2+1(當 l l l是奇數)。

證明:必要性顯然,鴿籠原理即可。充分性,只需證明確實存在一個方法即可。演算法如下:開一個字元陣列 c c c,先找到出現最多次數的字母,將其填到 c [ 0 ] , c [ 2 ] , c [ 4 ] , . . . , c [ 2 i ] c[0],c[2],c[4],...,c[2i] c[0],c[2],c[4],...,c[2i],然後找到出現次數第二多的字母,填到 c [ 2 i + 2 ] , c [ 2 i + 4 ] , . . . c[2i+2],c[2i+4],... c[2i+2],c[2i+4],...,如果填不下了,就從 c [ 1 ] , c [ 3 ] , c [ 5 ] , . . . c[1],c[3],c[5],... c[1],c[3],c[5],...這樣開始填。由於有上面所說的個數限制,導致不可能有相鄰的字母相同的情況發生。

程式碼實現方面,可以直接先填,然後驗證。挑出現最多的字母可以用最大堆來做。程式碼如下:

import java.util.PriorityQueue;

public class Solution {
    public String reorganizeString(String S) {
        char[] str = new char[S.length()];
        int[] count = new int[26];
        for (int i = 0; i < S.length(); i++) {
            count[S.charAt(i) - 'a']++;
        }
        
        PriorityQueue<Character> maxHeap = new PriorityQueue<>((c1, c2) -> -Integer.compare(count[c1 - 'a'], count[c2 - 'a']));
        for (char ch = 'a'; ch <= 'z'; ch++) {
            maxHeap.offer(ch);
        }
        
        int idx = 0;
        while (!maxHeap.isEmpty()) {
            char max = maxHeap.poll();
            while (count[max - 'a'] > 0) {
                str[idx] = max;
                count[max - 'a']--;
                idx += 2;
                // 出界了,從str[1]開始填
                if (idx >= str.length) {
                    idx = 1;
                }
            }
        }
    
    	// 如果有相鄰字母相同了,說明不存在合法解,返回空串
        for (int i = 1; i < str.length; i++) {
            if (str[i] == str[i - 1]) {
                return "";
            }
        }
        
        return new String(str);
    }
}

時空複雜度 O ( l ) O(l) O(l)

相關文章