Problem

Given a string s, rearrange the characters of s so that any two adjacent characters are not the same.

Return any possible rearrangement of s or return "" if not possible.

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

Example 1:

Input: s = "aab"
Output: "aba"

Example 2:

Input: s = "aaab"
Output: ""

Constraints:

  • 1 <= s.length <= 500
  • s consists of lowercase English letters.

Test Cases

1
2
class Solution:
def reorganizeString(self, s: str) -> str:
solution_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pytest

from solution import Solution


def verify(res, s):
assert sorted(res) == sorted(s)
prev = res[0]
for i in range(1, len(s)):
assert prev != res[i]
prev = res[i]


@pytest.mark.parametrize('s, expected', [
("aab", "aba"),
("aaab", ""),

("vvvlo", "vlvov"),
("aabbcc", "abacbc"),
])
@pytest.mark.parametrize('sol', [Solution()])
def test_solution(sol, s, expected):
res = sol.reorganizeString(s)
if expected == '':
assert res == ''
else:
assert res != ''
if res != expected:
verify(res, s)

Thoughts

判定是否可行很简单,显然如果有一个字符的出现次数大于 n/2\lceil n/2\rceil 就不可行,否则可行。

开始以为重新排列 s 的逻辑类似于 2182. Construct String With Repeat Limit,相当于 repeatLimit = 1,且不限定字符的顺序。但是因为要求是用掉所有的字符(当可行的时候),重建的逻辑就还是会有些不同。Problem 2182 中因为要让结果字符串按字典序最大,每次都先取最大的字符,却并不保证所有字符都能用上。

本题需要把所有的字符都用上,可以考虑建一个词频的最大堆(词频相同时按字符的字典序),每次从堆中取出词频最大的两个字符,添加到结果字符串末尾。把两个字符以及剩余的词频(如果还大于零)放回到堆中。因为词频相同的字符会按字典序排序,本次取出的第二个字符一定不会跟下一次取出的第一个字符相同,不会产生连续重复字符。

最后如果堆里只剩下一个字符(显然剩余的词频只能是 1),将其加到结果字符串的末尾即可。

时间复杂度 O(n log C),空间复杂度 O(C),其中 C 是 s 中各不相同的字符总数,题目中 C ≤ 26 可以认为是常数。

Code

solution.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from heapq import heapify, heappop, heappush, heapreplace
from typing import Counter


class Solution:
def reorganizeString(self, s: str) -> str:
counts = Counter(s)
if max(counts.values()) > ((len(s) + 1) >> 1):
return ''

parts = []
queue = [(-f, c) for c, f in counts.items()]
heapify(queue)
while len(queue) > 1:
f1, c1 = heappop(queue)
f2, c2 = queue[0]
parts.append(c1)
parts.append(c2)
f1 += 1
f2 += 1
if f2:
heapreplace(queue, (f2, c2))
heappush(queue, (f1, c1))
elif f1:
heapreplace(queue, (f1, c1))
else:
heappop(queue)

if queue:
parts.append(queue[0][1])

return ''.join(parts)