边工作边刷题:70天一遍leetcode: day 96-2
Rearrange String k Distance Apart
要点:这题要反向思维:如果k distance apart,那么除非最后一组,其他k组都是distinct的。greedy algorithm:显然出现次数越多,越要优先放,这样才能分布到尽可能多个streak。另外同个char最好间隔正好k。
- 二个考虑的问题:如何按次数一个streak一个streak generate?什么情况不够条件了返回?
- 如果每个streak都是distinct的,那么下一个放的元素要出heap,如果count不为0,再入heap来放下一个streak:这就类似level order traversal了,只不过当前层的遍历是固定的k
- 显然heap中(当前层)元素不够了,就说明不满足条件了
python coding
- python要用collections.Counter()来计数,不要loop了,产生Counter object,用items()来产生一个(key,count) pair list
https://repl.it/CaPy
另一种解法:上面的heapq解法是每个bucket一起填充,所以不断出heap再入heap。而另一种解法是优先把某个letter填到不同的bucket里。
-
component包括:nbins,idx list(用来标注每个bin填充的位置)。
-
公式:
- bucket是轮流填充的,所以bucket的i公式i=(i+1)%nbins
- nbins:(len-1)/k+1:死记,同时可以兼顾奇偶的情况
-
greedy的两点考虑:显然如果某个letter个数超过nbins,不符合。同时可能某个bin已经满了,所以新letter要放到下一个中。所以即使不同letter的个数超过k也可能。有可能其他都满了,发现最后只能和上一个重复letter放在同一个bucket里,这样也不符合。
-
错误点
- 公式2.1是%nbins,不是k
- 检查是否同一个bucket要用idx[i]-1而不是idx[i-1]
- Counter排序需要reverse
import collections
from heapq import *
class Solution(object):
def rearrangeString(self, str, k):
"""
........:type str: str
........:type k: int
........:rtype: str
........"""
lettercounts = collections.Counter(str)
countheap = []
for c, count in lettercounts.items():
heappush(countheap, (-count, c))
n = len(str)
res = []
while countheap:
ln = min(k, n)
nextround = []
for i in xrange(ln):
if not countheap:
return ""
count, c = heappop(countheap)
count+=1 # b/c count is negative
if count<0:
nextround.append((count, c))
res.append(c)
n -= 1
for count, c in nextround:
heappush(countheap, (count, c))
return ''.join(res)
s = Solution()
# "abacabcd"
print s.rearrangeString("aaadbbcc", 2)
# ""
print s.rearrangeString("aaabc", 3)
# "abcabc"
print s.rearrangeString("aabbcc", 3)
# Given a non-empty string str and an integer k, rearrange the string such that the same characters are at least distance k from each other.
# All input strings are given in lowercase letters. If it is not possible to rearrange the string, return an empty string "".
# Example 1:
# str = "aabbcc", k = 3
# Result: "abcabc"
# The same letters are at least distance 3 from each other.
# Example 2:
# str = "aaabc", k = 3
# Answer: ""
# It is not possible to rearrange the string.
# Example 3:
# str = "aaadbbcc", k = 2
# Answer: "abacabcd"
# Another possible answer is: "abcabcda"
# The same letters are at least distance 2 from each other.
# Credits:
# Special thanks to @elmirap for adding this problem and creating all test cases.
# Hide Company Tags Google
# Hide Tags Hash Table Heap Greedy
from collections import Counter
class Solution(object):
def rearrangeString(self, str, k):
"""
:type str: str
:type k: int
:rtype: str
"""
if k==0:return str
n = len(str)
nbins = (n-1)/k+1
def binsize(i, k, n):
return min(k, n-i*k)
counts = Counter(str).items()
counts.sort(key=lambda x: x[1], reverse=True) # error 3: should be reverse order
idx = [0]*nbins
i = 0
res = ['']*n
for c, m in counts:
if m>nbins:
return ""
for _ in xrange(m):
while idx[i]>=binsize(i, k, n):
i = (i+1)%nbins # error 1: nbins, not k
offset = i*k
# print i, offset, idx[i]
if idx[i]>0 and res[offset + idx[i]-1]==c: # error 2: idx[i]-1 not idx[i-1], i is bucket number
return ""
res[offset+idx[i]]=c
idx[i]+=1
i = (i+1)%nbins
# print res
return ''.join(res)
s = Solution()
# "abacabcd"
assert s.rearrangeString("aaadbbcc", 2)=="acababcd"
# ""
assert s.rearrangeString("aaabc", 3)==""
# "acbacb"
assert s.rearrangeString("aabbcc", 3)=="acbacb"