题目被 leetcode 归类为动态规划,苦思冥想,没有找到合适的问题定义。
决定首先穷举,借以理解解空间的结构。穷举解法:
/** * @Author Niuxy * @Date 2020/7/20 11:17 下午 * @Description 穷举 */ String longestStr = ""; public String longestDiverseString(int a, int b, int c) { longest(a, b, c, ""); return longestStr; } public final void longest(int a, int b, int c, String s) { longestStr = s.length() > longestStr.length() ? s : longestStr; if (a == 0 && b == 0 && c == 0) { return; } if (a > 0 && canInsert('a', s)) { longest(a - 1, b, c, s + "a"); } if (b > 0 && canInsert('b', s)) { longest(a, b - 1, c, s + "b"); } if (c > 0 && canInsert('c', s)) { longest(a, b, c - 1, s + "c"); } } private final boolean canInsert(char ch, String s) { int length = s.length(); if (length < 2) { return true; } char char0; if ((char0 = s.charAt(length - 1)) == s.charAt(length - 2)) { if (char0 == ch) { return false; } } return true; }
穷举函数就像一把散弹枪,在每个节点将所有可能性散射出去。直到三个元素都用完或者不可继续拼接,将结果数组进行记录,取全局最大值。
明显的,要从所有可能的组合中找最长的组合,问题的定义没有最优子结构性质。
D(a,b,c) 的定义不能是返回最长序列,上层问题无法根据该结果得出上层问题的最优解。
D(a,b,c) 的定义只能是尝试所有可能的路径,上层问题基于该结果拓展出本层的所有可能的路径,从中取最优值。
穷举路径进行搜索的函数没有建立缓存的必要,因为每条路径只会尝试一次。
这样避免重复计算的路就行不通了。
只能从另一个方向,避免无效计算来提升效率。
凭直觉,想要结果字符串最长,应该最先将存量最多的元素拼入字符串,用次多的元素分割最多的元素。
因为剩余元素中最多的元素越少,剩余元素可以组成的字符串也越长。
贪心解法:
class CharAndNum implements Comparable { char ch; int count; CharAndNum(char ch, int count) { this.ch = ch; this.count = count; } @Override public int compareTo(Object o) { CharAndNum other = (CharAndNum) o; return other.count-count; } } public String longestDiverseString1(int a, int b, int c) { CharAndNum[] chars = new CharAndNum[]{ new CharAndNum('a', a), new CharAndNum('b', b), new CharAndNum('c', c) }; StringBuilder sb = new StringBuilder(); while (true) { System.out.println(sb.toString()); Arrays.sort(chars); if (isEnd(chars, sb)) { break; } if (canInsert(sb, chars[0].ch)) { sb.append(chars[0].ch); chars[0].count--; } else if (chars[1].count > 0) { sb.append(chars[1].ch); chars[1].count--; } } return sb.toString(); } private final boolean isEnd(CharAndNum[] chars, StringBuilder sb) { if (chars[0].count == 0) { return true; } if (!canInsert(sb, chars[0].ch)) { if (chars[1].count == 0) { return true; } } return false; } private boolean canInsert(StringBuilder sb, char ch) { int length = sb.length(); if (length < 2) { return true; } char pre; if ((pre = sb.charAt(length - 1)) == sb.charAt(length - 2)) { if (pre == ch) { return false; } } return true; }