Leetcode 1405 最长快乐字符串 穷举与贪心

Leetcode 1405 最长快乐字符串 穷举与贪心 

  题目被 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;
    }

Leetcode 1405 最长快乐字符串 穷举与贪心