Construct String With Repeat Limit

medium heap greedy string

Problem

Given a string s and an integer repeatLimit, rearrange any subset of the letters of s (using each letter at most as many times as it occurs) into the lexicographically largest string such that no letter appears more than repeatLimit times in a row. You do not have to use every letter, but a valid answer always uses every letter exactly its full count, since dropping a letter never helps.

Inputs = "cczazcc", repeatLimit = 3
Output"zzcccac"
Counts are c:4, z:2, a:1. Lay down "zz", then "ccc" (hits the cap of 3 with a 'c' left over), break the run with one 'a', then place the final 'c'.

import heapq
def repeat_limited_string(s, repeat_limit):
    cnt = [0] * 26
    for ch in s:
        cnt[ord(ch) - 97] += 1
    h = [-i for i in range(26) if cnt[i] > 0]
    heapq.heapify(h)
    out = []
    while h:
        i = -heapq.heappop(h)
        take = min(cnt[i], repeat_limit)
        out.append(chr(i + 97) * take)
        cnt[i] -= take
        if cnt[i] > 0:
            if not h:
                break
            j = -h[0]
            out.append(chr(j + 97))
            cnt[j] -= 1
            if cnt[j] == 0:
                heapq.heappop(h)
            heapq.heappush(h, -i)
    return ''.join(out)
function repeatLimitedString(s, repeatLimit) {
  const cnt = new Array(26).fill(0);
  for (const ch of s) cnt[ch.charCodeAt(0) - 97]++;
  const heap = [];
  for (let i = 0; i < 26; i++) if (cnt[i] > 0) heap.push(i);
  heap.sort((x, y) => y - x);
  const out = [];
  while (heap.length) {
    const i = heap.shift();
    const take = Math.min(cnt[i], repeatLimit);
    out.push(String.fromCharCode(97 + i).repeat(take));
    cnt[i] -= take;
    if (cnt[i] > 0) {
      if (!heap.length) break;
      const j = heap[0];
      out.push(String.fromCharCode(97 + j));
      cnt[j]--;
      if (cnt[j] === 0) heap.shift();
      heap.unshift(i);
    }
  }
  return out.join('');
}
class Solution {
    public String repeatLimitedString(String s, int repeatLimit) {
        int[] cnt = new int[26];
        for (char ch : s.toCharArray()) cnt[ch - 'a']++;
        PriorityQueue<Integer> heap = new PriorityQueue<>((x, y) -> y - x);
        for (int i = 0; i < 26; i++) if (cnt[i] > 0) heap.offer(i);
        StringBuilder out = new StringBuilder();
        while (!heap.isEmpty()) {
            int i = heap.poll();
            int take = Math.min(cnt[i], repeatLimit);
            for (int k = 0; k < take; k++) out.append((char) ('a' + i));
            cnt[i] -= take;
            if (cnt[i] > 0) {
                if (heap.isEmpty()) break;
                int j = heap.peek();
                out.append((char) ('a' + j));
                cnt[j]--;
                if (cnt[j] == 0) heap.poll();
                heap.offer(i);
            }
        }
        return out.toString();
    }
}
string repeatLimitedString(string s, int repeatLimit) {
    vector<int> cnt(26, 0);
    for (char ch : s) cnt[ch - 'a']++;
    priority_queue<int> heap;
    for (int i = 0; i < 26; i++) if (cnt[i] > 0) heap.push(i);
    string out;
    while (!heap.empty()) {
        int i = heap.top();
        heap.pop();
        int take = min(cnt[i], repeatLimit);
        out.append(take, (char)('a' + i));
        cnt[i] -= take;
        if (cnt[i] > 0) {
            if (heap.empty()) break;
            int j = heap.top();
            out.push_back((char)('a' + j));
            cnt[j]--;
            if (cnt[j] == 0) heap.pop();
            heap.push(i);
        }
    }
    return out;
}
Time: O(n + k log k) Space: O(k)