题目

类型:贪心
image.png

解题思路

每次都取当前剩余次数最多的字符来进行构造(前提是满足「不出现形如 aaa 字符串」的要求)

可以使用「优先队列(堆)」来实现上述过程,以 (字符编号, 字符剩余数量) 的二元组形式进行存储,构建以 字符剩余数量 排倒序的「大根堆」:

  • 起始先将 (0, a)、(1, b) 和 (2, c) 进行入堆(其中 123 为字符编号,代指 abc,同时规定只有剩余数量大于 0 才能入堆);
  • 每次取出堆顶元素(剩余数量最多的字符),尝试参与答案的构造:
    • 不违反连续三个字符相同:则说明当前字符能够追加到当前答案尾部,若追加后还有字符剩余,则更新剩余数量重新入堆;
    • 违反连续三个字符相同:说明该字符无法追加到当前答案尾部,此时尝试从堆中取出剩余次数次大的字符(若当前堆为空,说明没有任何合法字符能够追加,直接 break),若次大字符追加后还有字符剩余,则更新剩余数量重新入堆,同时将此前取的最大字符元祖也重新入堆;
  • 重复步骤 2,直到所有字符均被消耗,或循环提前结束。

该做法的正确性:当 最长快乐字符串 - 图2 时能够确保所有字符轮流参与构建,得到长度最大的快乐字符串,而该贪心策略(每次尽可能地进行大数消减)可以确保能够尽可能的凑成 a = b = c 的局面,并且凑成该局面过程中不会从有解变为无解。

代码

  1. class Solution {
  2. public String longestDiverseString(int a, int b, int c) {
  3. PriorityQueue<int[]> q = new PriorityQueue<>((x,y)->y[1]-x[1]);
  4. if (a > 0) q.add(new int[]{0, a});
  5. if (b > 0) q.add(new int[]{1, b});
  6. if (c > 0) q.add(new int[]{2, c});
  7. StringBuilder sb = new StringBuilder();
  8. while (!q.isEmpty()) {
  9. int[] cur = q.poll();
  10. int n = sb.length();
  11. if (n >= 2 && sb.charAt(n - 1) - 'a' == cur[0] && sb.charAt(n - 2) - 'a' == cur[0]) {
  12. if (q.isEmpty()) break;
  13. int[] next = q.poll();
  14. sb.append((char)(next[0] + 'a'));
  15. if (--next[1] != 0) q.add(next);
  16. q.add(cur);
  17. } else {
  18. sb.append((char)(cur[0] + 'a'));
  19. if (--cur[1] != 0) q.add(cur);
  20. }
  21. }
  22. return sb.toString();
  23. }
  24. }