{x}
blog image

Longest Happy String

A string s is called happy if it satisfies the following conditions:

  • s only contains the letters 'a', 'b', and 'c'.
  • s does not contain any of "aaa", "bbb", or "ccc" as a substring.
  • s contains at most a occurrences of the letter 'a'.
  • s contains at most b occurrences of the letter 'b'.
  • s contains at most c occurrences of the letter 'c'.

Given three integers a, b, and c, return the longest possible happy string. If there are multiple longest happy strings, return any of them. If there is no such string, return the empty string "".

A substring is a contiguous sequence of characters within a string.

 

Example 1:

Input: a = 1, b = 1, c = 7
Output: "ccaccbcc"
Explanation: "ccbccacc" would also be a correct answer.

Example 2:

Input: a = 7, b = 1, c = 0
Output: "aabaa"
Explanation: It is the only correct answer in this case.

 

Constraints:

  • 0 <= a, b, c <= 100
  • a + b + c > 0

Solution Explanation: Longest Happy String

This problem asks to construct the longest possible string containing only 'a', 'b', and 'c', such that no three consecutive characters are the same and the counts of 'a', 'b', and 'c' do not exceed given limits (a, b, c respectively).

Approach: Greedy with Priority Queue

The most efficient approach is a greedy algorithm that utilizes a priority queue (or similar data structure like a max heap). The core idea is to always choose the character with the highest available count, unless doing so would violate the "no three consecutive identical characters" rule.

Algorithm:

  1. Initialization: Create a priority queue to store the characters ('a', 'b', 'c') along with their remaining counts. The priority queue is ordered such that the character with the largest remaining count has the highest priority.

  2. Iteration: While the priority queue is not empty:

    • Dequeue the character with the highest priority (largest remaining count).
    • Check if adding this character would violate the "no three consecutive identical characters" rule. This is done by checking the last two characters in the string being built.
    • If adding the character would violate the rule:
      • If there are other characters in the queue, dequeue the next highest priority character and add it to the string.
      • Put the original character back into the queue.
    • If adding the character is allowed:
      • Add the character to the string.
      • If the character's count is greater than 1, put it back into the queue with the updated count.
  3. Return: Return the constructed string.

Time and Space Complexity

  • Time Complexity: O(N log K), where N is the total number of characters in the final string (approximately a + b + c) and K is the number of characters (3 in this case). The log K factor comes from the priority queue operations (insertion and deletion).

  • Space Complexity: O(K), dominated by the space used by the priority queue to store the characters and their counts. In this specific problem, K is a constant (3).

Code Examples (with explanations)

The provided code examples in Python, Java, C++, Go, and TypeScript all implement this greedy approach using a priority queue. Let's examine a representative example (Python):

import heapq
 
class Solution:
    def longestDiverseString(self, a: int, b: int, c: int) -> str:
        h = []
        if a > 0:
            heapq.heappush(h, (-a, 'a')) # Use negative count for max-heap behavior
        if b > 0:
            heapq.heappush(h, (-b, 'b'))
        if c > 0:
            heapq.heappush(h, (-c, 'c'))
 
        ans = []
        while h:
            count1, char1 = heapq.heappop(h)
            if len(ans) >= 2 and ans[-1] == char1 and ans[-2] == char1:
                if not h:
                    break  # No other characters available
                count2, char2 = heapq.heappop(h)
                ans.append(char2)
                if count2 + 1 < 0: #Check if count is >1 after decreasing it
                    heapq.heappush(h, (count2 + 1, char2))
                heapq.heappush(h, (count1, char1))
            else:
                ans.append(char1)
                if count1 + 1 < 0:
                    heapq.heappush(h, (count1 + 1, char1))
        return "".join(ans)
 

Explanation of Python code:

  • heapq.heappush: Adds elements to the min-heap. We use negative counts to simulate a max-heap (largest count at the top).
  • heapq.heappop: Removes and returns the element with the highest priority (largest negative count).
  • The if len(ans) >= 2 ... block handles the rule against three consecutive identical characters.
  • The code efficiently manages the counts, adding characters back to the heap if they still have counts greater than 1.

The other language examples follow a very similar structure, adapting the priority queue implementation to the specific language's features. The core algorithm remains the same, ensuring an efficient and correct solution to the problem.