Problem: Construct String With Repeat Limit

You are given a string `s` and an integer `repeatLimit`. Construct a new
string `repeatLimitedString` using the characters of `s` such that no letter
appears more than `repeatLimit` times in a row. You do not have to use all
characters from `s`.

Return the lexicographically largest `repeatLimitedString` possible.

A string `a` is lexicographically larger than a string `b` if in the first
position where `a` and `b` differ, string `a` has a letter that appears later
in the alphabet than the corresponding letter in `b`. If the first
`min(a.length, b.length)` characters do not differ, then the longer string is
the lexicographically larger one.


Python solution:
import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def repeatLimitedString(self, s: str, repeatLimit: int) -> str:
    ans = ''
    count = collections.Counter(s)

    while True:
      addOne = ans and self._shouldAddOne(ans, count)
      c = self._getLargestChar(ans, count)
      if c == ' ':
        break
      repeats = 1 if addOne else min(count[c], repeatLimit)
      ans += c * repeats
      count[c] -= repeats

    return ans

  def _shouldAddOne(self, ans: str, count: collections.Counter) -> bool:
    for c in reversed(string.ascii_lowercase):
      if count[c]:
        return ans[-1] == c
    return False

  def _getLargestChar(self, ans: str, count: collections.Counter) -> int:
    for c in reversed(string.ascii_lowercase):
      if count[c] and (not ans or ans[-1] != c):
        return c
    return ' '


Java solution:
class Solution {
  public String repeatLimitedString(String s, int repeatLimit) {
    StringBuilder sb = new StringBuilder();
    int[] count = new int[26];

    for (final char c : s.toCharArray())
      ++count[c - 'a'];

    while (true) {
      final boolean addOne = !sb.isEmpty() && shouldAddOne(sb, count);
      final int i = getLargestChar(sb, count);
      if (i == -1)
        break;
      final int repeats = addOne ? 1 : Math.min(count[i], repeatLimit);
      sb.append(String.valueOf((char) ('a' + i)).repeat(repeats));
      count[i] -= repeats;
    }

    return sb.toString();
  }

  private boolean shouldAddOne(StringBuilder sb, int[] count) {
    for (int i = 25; i >= 0; --i)
      if (count[i] > 0)
        return sb.charAt(sb.length() - 1) == 'a' + i;
    return false;
  }

  private int getLargestChar(StringBuilder sb, int[] count) {
    for (int i = 25; i >= 0; --i)
      if (count[i] > 0 && (sb.isEmpty() || sb.charAt(sb.length() - 1) != 'a' + i))
        return i;
    return -1;
  }
}


C++ solution:
class Solution {
 public:
  string repeatLimitedString(string s, int repeatLimit) {
    string ans;
    vector<int> count(26);

    for (const char c : s)
      ++count[c - 'a'];

    while (true) {
      const bool addOne = !ans.empty() && shouldAddOne(ans, count);
      const int i = getLargestChar(ans, count);
      if (i == -1)
        break;
      const int repeats = addOne ? 1 : min(count[i], repeatLimit);
      ans += string(repeats, 'a' + i);
      count[i] -= repeats;
    }

    return ans;
  }

 private:
  bool shouldAddOne(const string& ans, const vector<int>& count) {
    for (int i = 25; i >= 0; --i)
      if (count[i])
        return ans.back() == 'a' + i;
    return false;
  }

  int getLargestChar(const string& ans, const vector<int>& count) {
    for (int i = 25; i >= 0; --i)
      if (count[i] && (ans.empty() || ans.back() != 'a' + i))
        return i;
    return -1;
  }
};