Solved·28 Aug
Longest Happy String
DifficultyMedium
PatternTwo Heap
TrackDSA
tl;dr
A string s is called happy if it satisfies the following conditions:
full write-up
1405. Longest Happy String
Difficulty: Medium
Link: LeetCode Problem
Problem Statement
A string s is called happy if it follows all these rules:
sonly has the letters'a','b', and'c'.sdoes not contain"aaa","bbb", or"ccc"as a substring.shas at mostacopies of the letter'a'.shas at mostbcopies of the letter'b'.shas at mostccopies of the letter'c'.
You are given three numbers: a, b, and c.
Return the longest possible happy string. If more than one longest string works, you can return any one of them. If no happy string can be made, return an empty string "".
A substring means letters that sit next to each other in the string, in order.
Examples
Example 1
Input: a = 1, b = 1, c = 7
Output: "ccaccbcc"
Another correct answer would be "ccbccacc".
Example 2
Input: a = 7, b = 1, c = 0
Output: "aabaa"
This is the only correct answer for this case.
Solution
The steps to solve this problem:
- We create a max heap. We push each letter into the heap along with how many times it can still be used.
- We pop the letter with the highest remaining count from the heap.
- Before adding it to our result, we check: would this letter appear three times in a row? We check the last two letters already in our result.
- If it does not create three in a row, we add the letter to the result. We reduce its count by 1, and push it back into the heap if its count is still more than 0.
- If it would create three in a row, we cannot use it right now. So:
- If the heap is empty, there is no other letter to use. We stop here.
- If the heap is not empty, we pop the next-highest letter instead. We add that letter to the result, reduce its count, and push it back if needed.
- We also push the first letter (the one we skipped) back into the heap, unused, so we can try it again later.
- We repeat this process until the heap is empty.
Code
class MaxHeap {
constructor(comparator) {
this.heap = [];
this.comparator = comparator;
}
size() { return this.heap.length; }
isEmpty() { return this.heap.length === 0; }
peek() { return this.heap[0]; }
push(val) {
this.heap.push(val);
this._bubbleUp(this.heap.length - 1);
}
pop() {
const top = this.heap[0];
const last = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = last;
this._bubbleDown(0);
}
return top;
}
_bubbleUp(i) {
while (i > 0) {
const parent = (i - 1) >> 1;
if (this.comparator(this.heap[parent], this.heap[i]) >= 0) break;
[this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]];
i = parent;
}
}
_bubbleDown(i) {
const n = this.heap.length;
while (true) {
const left = 2 * i + 1, right = 2 * i + 2;
let best = i;
if (left < n && this.comparator(this.heap[left], this.heap[best]) > 0) best = left;
if (right < n && this.comparator(this.heap[right], this.heap[best]) > 0) best = right;
if (best === i) break;
[this.heap[i], this.heap[best]] = [this.heap[best], this.heap[i]];
i = best;
}
}
}
var longestDiverseString = function(a, b, c) {
const heap = new MaxHeap((x, y) => x[0] - y[0]); // compare by count
if (a > 0) heap.push([a, 'a']);
if (b > 0) heap.push([b, 'b']);
if (c > 0) heap.push([c, 'c']);
let result = [];
while (!heap.isEmpty()) {
const [count, letter] = heap.pop();
const len = result.length;
// Check if using this letter would create three-in-a-row
if (len >= 2 && result[len - 1] === letter && result[len - 2] === letter) {
// Can't use this letter right now — try the next-highest instead
if (heap.isEmpty()) break; // no alternative letter available, must stop
const [count2, letter2] = heap.pop();
result.push(letter2);
if (count2 - 1 > 0) heap.push([count2 - 1, letter2]);
// put the original letter back, unused, for next round
heap.push([count, letter]);
} else {
result.push(letter);
if (count - 1 > 0) heap.push([count - 1, letter]);
}
}
return result.join('');
};