Naive :
from heapq import heappush, heappop
class Solution:
# time(N) space O(N)
def leastInterval(self, tasks: List[str], n: int) -> int:
size = len(tasks)
if not n:
return size
# step1: count times of each task, get the task with max
cnt = collections.Counter(tasks)
# step2: push cnt,task into PQ
hp,cur_time = [],0
for task,ct in cnt.items():
heappush(hp,(-ct,task))
#step3: running heap until tasks inside runs out
while hp:
# why tmp list: make sure task with same cnt will not conflict each other
idx,tmp = 0, []
# idx: mark position at single interval => A _ _ , n = 2, idx= 0, 1, 2
# step4: for each valid slot set(minimum at n interval ), each time do heap operation, increment time by 1
while idx <= n:
# cur_time record assigned task length so far
cur_time += 1
if hp:
# step4.1: pop out task and its cnt off PQ
time, taskid = heappop(hp)
# step4.2 check if cnt for taskid has run out, if not, push task and updated ask to tmp list
if time != -1:
tmp.append((time+1,taskid))
# step4.3 check if hp and tmp run out , if not idx + 1
if not hp and not tmp:
break
# here is to guaranteen each task has interval n . e.g. hp: [] tmp:[(-2,A)] and before push (-2,A) back to hq, need to make sure idx has reached to n
else:
idx += 1
# step5: push updated task and cnt back to hp, reset idx and tmp, go to next loop again
for item in tmp:
heappush(hp,item)
# step6, return current time
return cur_time
Optmization : Greedy
class Solution:
# time O(N) space O(1) since 26 ~ 1
def leastInterval(self, tasks: List[str], n: int) -> int:
feq = [0] *26
for t in tasks:
feq[ord(t)-ord('A')] += 1
feq.sort()
frq_max = feq.pop()
idle_slots = (frq_max - 1) * n
while feq:
# why frq_max -1 ? because freq.pop() <= frq_max; if freq.pop() == frq_max , the task just need to ocupy frq_max-1 since last task will idle always.
idle_slots -= min(feq.pop(),frq_max-1)
idle_slots = max(0,idle_slots)
return len(tasks) + idle_slots